Ansible - Setting up an Ansible Control Machine

25 minute read

In this post we will be setting up an Ansible Control Machine to execute our Ansible tasks from. This server will not have writable access to our Git repos so no changes can be made and pushed up. For our Ansible Control Machine we will be using an Ubuntu 14.04 LTS server.

The focus of this was to setup a control machine in order to test and push code based on a specific Ansible release. This was mainly due to the release of 2.0 in which I have ran into several issues as far as backwards compatibility and I do not want to go through and make changes so soon on roles and such that have been working for quite some time now. And also as I begin training others and learning the Infra-As-Code journey.

If you are interested in testing this out in a Vagrant environment I have put together a repo for going through these series of posts to come. This Vagrant environment will spin up a Gerrit Code Review server and a client in which you can follow along with throughout this post. You can view the GitHub repo here.

NOTE - This post will be updated over time so it may or not be complete at the time of reading.

To install python follow the below for your OS.

Ubuntu:

sudo apt-get update
sudo apt-get install python-setuptools python-dev libffi-dev libssl-dev git sshpass tree
sudo easy_install pip
```bash

`OSX`:
You will need to install [Xcode](https://developer.apple.com/xcode/)
developers kit first.
Next you will need to install [Homebrew](http://brew.sh/).

```bash
ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
```text

Now you can install python.

```bash
sudo easy_install pip
```bash

Now with the above out of the way we are ready to begin.

```bash
sudo -H pip uninstall virtualenv
sudo -H pip uninstall virtualenvwrapper
sudo -H pip install virtualenv
sudo -H pip install virtualenvwrapper --ignore-installed six
sudo -H pip install httplib2
mkdir ~/.virtualenvs
```bash

If not using zsh and standard bash

```bash
echo "source "$(which virtualenvwrapper.sh) >> ~/.profile
echo "export WORKON_HOME=~/.virtualenvs" >> ~/.profile
source ~/.profile
```bash

If using zsh

```bash
echo "source "$(which virtualenvwrapper.sh) >> ~/.zshrc
echo "export WORKON_HOME=~/.virtualenvs" >> ~/.zshrc
source ~/.zshrc
```sql

Now let's create our Python virtual environments for testing different
versions of Ansible.

```bash
mkdir ~/ansible_virtualenvs

cd ~/ansible_virtualenvs
mkdir 1.9.4
cd 1.9.4
mkvirtualenv ansible-1.9.4
pip install ansible==1.9.4
ansible --version
deactivate

cd ~/ansible_virtualenvs
mkdir 1.9.5
cd 1.9.5
mkvirtualenv ansible-1.9.5
pip install ansible==1.9.5
ansible --version
deactivate

cd ~/ansible_virtualenvs
mkdir 1.9.6
cd 1.9.6
mkvirtualenv ansible-1.9.6
pip install ansible==1.9.6
ansible --version
deactivate

cd ~/ansible_virtualenvs
mkdir 2.0.0.1
cd 2.0.0.1
mkvirtualenv ansible-2.0.0.1
pip install ansible==2.0.0.1
ansible --version
deactivate

cd ~/ansible_virtualenvs
mkdir 2.0.0.2
cd 2.0.0.2
mkvirtualenv ansible-2.0.0.2
pip install ansible==2.0.0.2
ansible --version
deactivate

cd ~/ansible_virtualenvs
mkdir 2.0.1.0
cd 2.0.1.0
mkvirtualenv ansible-2.0.1.0
pip install ansible==2.0.1.0
ansible --version
deactivate

cd ~/ansible_virtualenvs
mkdir 2.0.2.0
cd 2.0.2.0
mkvirtualenv ansible-2.0.2.0
pip install ansible==2.0.2.0
ansible --version
deactivate

cd ~/ansible_virtualenvs
mkdir 2.1.0.0
cd 2.1.0.0
mkvirtualenv ansible-2.1.0.0
pip install ansible==2.1.0.0
ansible --version
deactivate
```bash

Now we are ready to start working within our different Ansible version
environments.

So let's first setup our 1.9.4 environment.

```bash
workon ansible-1.9.4
pip install pyopenssl ndg-httpsclient pyasn1 pysphere pyvmomi git-review
```bash

Now we will setup our 2.0 environment.

```bash
deactivate

workon ansible-2.0
pip install pyopenssl ndg-httpsclient pyasn1 pysphere pyvmomi git-review
```sql

Now let's create our Git Projects directory to store all of our Git
Projects and code.

```bash
mkdir ~/Git_Projects
cd ~/Git_Projects
```bash

Now we are ready to start populating our GitHub repo(s) with the code we
want to use and/or just view.

I have a GitHub repo that has some Ansible tasks that will allow us to
pull down our specific required repos.

```bash
cd ~/Git_Projects
git clone https://github.com/mrlesmithjr/ansible-clone-git-repos.git
```bash

Once the code is pulled down you can modify the defaults/main.yml file
to comment out or add additional GitHub user(s) repos to pull down.

```bash
cd ansible-clone-git-repos
vi defaults/main.yml
```text

You will see the default is the following:

```yaml
github_users:  #define github user(s) to clone repos from
  - bunchc
  - debops
  - lowescott
  - Mierdin
  - mrlesmithjr
  - dstamen
  - phpipam
```text

I will just be pulling down my own personal repos so I will comment out
the other as such:

```yaml
github_users:  #define github user(s) to clone repos from
##  - bunchc
##  - debops
##  - lowescott
##  - Mierdin
  - mrlesmithjr
##  - dstamen
##  - phpipam
```bash

Now save the file.

Let's now use our Ansible virtual environment of 1.9.4 here on out. (
We can easily switch between Ansible versions in order to conduct
testing from our same control machine. )

```bash
workon ansible-1.9.4
./clone_git_repos.sh
```bash

Once all of the repos have been pulled down you can take a look at them.

```bash
cd ../GitHub/mrlesmithjr
ls
```bash

And below is a script you can run to perform all of the steps above if
you want to take the easy way :)

```bash
#!/bin/bash
sudo apt-get update
sudo apt-get -y install python-setuptools python-dev libffi-dev libssl-dev git sshpass
sudo easy_install pip

sudo -H pip uninstall -y virtualenv
sudo -H pip uninstall -y virtualenvwrapper
sudo -H pip install virtualenv
sudo -H pip install virtualenvwrapper --ignore-installed six
sudo -H pip install httplib2

rm -rf ~/.virtualenvs
mkdir ~/.virtualenvs
echo "source /usr/local/bin/virtualenvwrapper.sh" >> ~/.profile
echo "export WORKON_HOME=~/.virtualenvs" >> ~/.profile
source ~/.profile

rm -rf ~/ansible_virtualenvs
mkdir ~/ansible_virtualenvs

cd ~/ansible_virtualenvs
mkdir 1.9.4
cd 1.9.4
mkvirtualenv ansible-1.9.4
pip install ansible==1.9.4
ansible --version
deactivate

cd ~/ansible_virtualenvs
mkdir 1.9.5
cd 1.9.5
mkvirtualenv ansible-1.9.5
pip install ansible==1.9.5
ansible --version
deactivate

cd ~/ansible_virtualenvs
mkdir 1.9.6
cd 1.9.6
mkvirtualenv ansible-1.9.6
pip install ansible==1.9.6
ansible --version
deactivate

cd ~/ansible_virtualenvs
mkdir 2.0.0.1
cd 2.0.0.1
mkvirtualenv ansible-2.0.0.1
pip install ansible==2.0.0.1
ansible --version
deactivate

cd ~/ansible_virtualenvs
mkdir 2.0.0.2
cd 2.0.0.2
mkvirtualenv ansible-2.0.0.2
pip install ansible==2.0.0.2
ansible --version
deactivate

cd ~/ansible_virtualenvs
mkdir 2.0.1.0
cd 2.0.1.0
mkvirtualenv ansible-2.0.1.0
pip install ansible==2.0.1.0
ansible --version
deactivate

cd ~/ansible_virtualenvs
mkdir 2.0.2.0
cd 2.0.2.0
mkvirtualenv ansible-2.0.2.0
pip install ansible==2.0.2.0
ansible --version
deactivate

cd ~/ansible_virtualenvs
mkdir 2.1.0.0
cd 2.1.0.0
mkvirtualenv ansible-2.1.0.0
pip install ansible==2.1.0.0
ansible --version
deactivate

mkdir ~/Git_Projects
cd ~/Git_Projects
git clone https://github.com/mrlesmithjr/ansible-clone-git-repos.git

cd ansible-clone-git-repos
sed -i -e 's|- bunchc|#- bunchc|' defaults/main.yml
sed -i -e 's|- debops|#- debops|' defaults/main.yml
sed -i -e 's|- lowescott|#- lowescott|' defaults/main.yml
sed -i -e 's|- Mierdin|#- Mierdin|' defaults/main.yml
sed -i -e 's|- dstamen|#- dstamen|' defaults/main.yml
sed -i -e 's|- phpipam|#- phpipam|' defaults/main.yml

workon ansible-1.9.4
./clone_git_repos.sh
cd ../GitHub/mrlesmithjr
ls
```sql

Now that we have some roles downloaded we can begin using them but we
first need to decide how we will leverage using these roles. There are
several ways that we can do this of course. But now is the time to make
that decision.

By default even in our Ansible virtual environment our roles would be
installed and looked for in the normal directory of
**_/etc/ansible/roles_**.

There are reasons to store all of your roles there and also there are
reasons not to. I am only providing what I find to be the best for my
environment(s). I personally choose to store all of my roles within a
folder called roles which would be part of my actual project. If you are
looking for a way to consolidate roles and ensure that all projects use
the same common roles that works very well too. I do this by collecting
all of my roles into an internal Git repo called
**ansible-common-core-roles**. And when I create a new project I pull
these roles from my internal Git repo as below:

```bash
git clone git@gitserver:/ansible-common-core-roles.git roles
```bash

Now I can leverage the same roles throughout each project as well as
others can too. I also do this for common variables and inventory to be
used over and over again for consistency. I use a repo called
**ansible-environments** and when I need those I will pull them down as
below:

```bash
git clone git@gitserver:/ansible-environments.git environments
```bash

Now when I need to run a play I can leverage all of these common roles
and variables as below:

```bash
ansible-playbook -i environments/production/site/inventory playbook.yml
```bash

As you can from above I reference production/site which is where we can
store site specific group_vars and host_vars. This allows for
variables to be site specific.

Another method that I use is when I might want to create a semi-private
repository to ensure that I or someone else has not updated the
ansible-common-core-roles. Or I need a role that is specific to that
project. Keep in mind I may still want to leverage upstream Git repos
for my roles but I want to ensure they are only updated when I choose.
For this method I will show an example of how to set this up.

First we need to create our directory structure for our LAMP Stack
project.

```bash
cd ~/Git_Projects
mkdir -p Lab/LAMP
cd Lab/LAMP
mkdir environments roles
git init
```sql

Now for this project in order to define where my roles are located I
will create an **ansible.cfg** file to use. Keep in mind by default when
running an Ansible playbook it will also look in the current directory
for a roles directory. I am doing this to ensure that I am using this
specific roles directory and not the default **/etc/ansible/roles**
directory. This will ensure that if I or someone else placed a role in
that directory it will not accidentally be used. Just a precaution.

So let's create our ansible.cfg file in our project folder. You can
check out the additional options you can use in your **ansible.cfg**
[here](http://docs.ansible.com/ansible/intro_configuration.html). Keep
in mind this will only apply any differences in the default
**ansible.cfg** which is installed as part of the Ansible package.

```bash
vi ansible.cfg
```yaml

And we will add the following to this file.

```bash
[defaults]
roles_path = ./roles
```yaml

We can also add multiple roles_path locations if we desire that as well
by doing the following:

```bash
roles_path = ./roles:/opt/ansible/roles
```sql

But we will only defining one path for this project.

Now we are ready to being creating our project. So first we know that
for a LAMP stack we will need Apache, MySQL and PHP/Python/Perl. Well in
this example I know that from the roles we pulled down from my GitHub
repo have those roles so I will leverage those for this project.

So first thing I will do is create a **requirements.yml** file which is
where we will define which roles from different resources I might want
to leverage.

```bash
vi requirements.yml
```yaml

And we will add the following bit of information to this file.

```yaml
---
- src: https://github.com/mrlesmithjr/ansible-apache2.git
- src: https://github.com/mrlesmithjr/ansible-mysql.git
```bash

The above states that we will be pulling the repos defined. And in order
to pull these roles down we will run the following:

```bash
ansible-galaxy install -r requirements.yml
```bash

Once that has completed we can ensure that we did indeed pull down the
roles we defined and they were installed to our roles directory as
defined in our **ansible.cfg** file.

```bash
cd roles
ls

(ansible-1.9.4)remote@ansible-control:~/Git_Projects/Lab/LAMP/roles$ ls
ansible-apache2  ansible-mysql
(ansible-1.9.4)remote@ansible-control:~/Git_Projects/Lab/LAMP/roles$
```yaml

So we did indeed install them as required and they were placed in the
correct folder. Now say we want to ensure we have the most current roles from our
upstream Git repo and we executed the same command as we did previously:

```bash
(ansible-1.9.4)remote@ansible-control:~/Git_Projects/Lab/LAMP$ ansible-galaxy install -r requirements.yml
- executing: git clone https://github.com/mrlesmithjr/ansible-apache2.git ansible-apache2
- executing: git archive --prefix=ansible-apache2/ --output=/tmp/tmp8Xzo8j.tar HEAD
- extracting ansible-apache2 to ./roles/ansible-apache2
- error: the specified role ansible-apache2 appears to already exist. Use --force to replace it.
- ansible-apache2 was NOT installed successfully.
- you can use --ignore-errors to skip failed roles.
(ansible-1.9.4)remote@ansible-control:~/Git_Projects/Lab/LAMP$
```yaml

As you can see we received errors stating that the roles already exist.
So we can get around this if we force them to be installed again by
doing the following:

```bash
(ansible-1.9.4)remote@ansible-control:~/Git_Projects/Lab/LAMP$ ansible-galaxy install -r requirements.yml -f
- executing: git clone https://github.com/mrlesmithjr/ansible-apache2.git ansible-apache2
- executing: git archive --prefix=ansible-apache2/ --output=/tmp/tmp6JgwxN.tar HEAD
- extracting ansible-apache2 to ./roles/ansible-apache2
- ansible-apache2 was installed successfully
- executing: git clone https://github.com/mrlesmithjr/ansible-mysql.git ansible-mysql
- executing: git archive --prefix=ansible-mysql/ --output=/tmp/tmpkc5W2l.tar HEAD
- extracting ansible-mysql to ./roles/ansible-mysql
- ansible-mysql was installed successfully
(ansible-1.9.4)remote@ansible-control:~/Git_Projects/Lab/LAMP$
```yaml

Now we have success and we now have the latest roles from our upstream
Git repo using our **requirements.yml** file. You could also easily just
pull down roles without using a **requirements.yml** file by doing the
following:

```bash
cd roles
git clone https://github.com/mrlesmithjr/ansible-apache2.git
git clone https://github.com/mrlesmithjr/ansible-mysql.git
```bash

Now in order to pull the latest code you can change into the roles/role
directory and do the following:
We will be checking the ansible-apache2 role.

```bash
cd roles/ansible-apache2
git pull

(ansible-1.9.4)remote@ansible-control:~/Git_Projects/Lab/LAMP/roles/ansible-apache2$ git pull
Already up-to-date.
(ansible-1.9.4)remote@ansible-control:~/Git_Projects/Lab/LAMP/roles/ansible-apache2$
```sql

As you can see the role is already up-to-date. One reason not to do this
(as far as my experience has been) is that because this is part of
another Git repo, if we store our project on an internal Git repo, when
we push our code up it will not include any directories that are pulled
from another upstream repo. So I generally will just use a
**requirements.yml** file and update the roles this way. Remember before
doing that you may want to create a new Git branch for testing first.

```bash
git checkout -b test

(ansible-1.9.4)remote@ansible-control:~/Git_Projects/Lab/LAMP$ git checkout -b test
Switched to a new branch 'test'
(ansible-1.9.4)remote@ansible-control:~/Git_Projects/Lab/LAMP$

ansible-galaxy install -r requirements.yml -f

(ansible-1.9.4)remote@ansible-control:~/Git_Projects/Lab/LAMP$ ansible-galaxy install -r requirements.yml -f
- executing: git clone https://github.com/mrlesmithjr/ansible-apache2.git ansible-apache2
- executing: git archive --prefix=ansible-apache2/ --output=/tmp/tmp2HWi5u.tar HEAD
- extracting ansible-apache2 to ./roles/ansible-apache2
- ansible-apache2 was installed successfully
- executing: git clone https://github.com/mrlesmithjr/ansible-mysql.git ansible-mysql
- executing: git archive --prefix=ansible-mysql/ --output=/tmp/tmp55rV1S.tar HEAD
- extracting ansible-mysql to ./roles/ansible-mysql
- ansible-mysql was installed successfully
(ansible-1.9.4)remote@ansible-control:~/Git_Projects/Lab/LAMP$
```yaml

Now this is not much use for our internal Git repo at this time because
we have not set that up yet. I will leave this up to you to decide on
your internal Git repo server and setup. I choose to use Gerrit because
I want code review abilities. If you are interested in setting up a
Gerrit server I have an **ansible-gerrit** role that can be leveraged
for that as well. You can view this in the
~/**Git_Projects/GitHub/mrlesmithjr/ansible-gerrit** directory that we
pulled down in the beginning of this post. _( I will be publishing
another post on setting up a Gerrit server later and will reference it
from here if interested. )_

```bash
cd ~/Git_Projects/GitHub/mrlesmithjr/ansible-gerrit/
```sql

Now let's create an Ansible playbook to leverage our roles for our LAMP
project.

Before we begin let's ensure that we are in our LAMP project directory.

```bash
cd ~/Git_Projects/Lab/LAMP/
```sql

Now we will create a playbook.yml file as below:

```bash
vi playbook.yml
```yaml

```yaml
---
- name: Build LAMP Servers
  hosts: lamp-servers
  become: true
  vars:
    - install_php: true
  roles:
    - role: ansible-apache2
    - role: ansible-mysql
  tasks:
```sql

Now with our playbook created we need to create our inventory file and
our group_vars and host_vars.

```bash
cd environments
mkdir -p dev/group_vars/all dev/host_vars prod/group_vars/all prod/host_vars test/group_vars/all test/host_vars
```bash

To see all of our environment folders we just created:

```bash
tree

(ansible-1.9.4)vagrant@client:~/Git_Projects/Lab/LAMP/environments$ tree
.
├── dev
│   ├── group_vars
│   │   └── all
│   └── host_vars
├── prod
│   ├── group_vars
│   │   └── all
│   └── host_vars
└── test
    ├── group_vars
    │   └── all
    └── host_vars

12 directories, 0 files
(ansible-1.9.4)vagrant@client:~/Git_Projects/Lab/LAMP/environments$
```bash

We will work within our dev environment here.

```bash
cd environments/dev
```sql

Now create your inventory file.

```bash
vi inventory
```bash

And add the following:

```bash
[lamp-servers]
lamp-01
lamp-02
```bash

Now if you look at our playbook we defined a variable there which is
install_php: true we could also not define this variable within our
playbook but within our group_vars for our dev environment.

```bash
cd group_vars
mkdir lamp-servers
cd lamp-servers
vi apache.yml
```yaml

Now paste the following:

```yaml
---
install_php: true
```bash

Now we can remove that variable definition from our playbook as our
group_vars will now cover that variable.

```bash
cd ../../../../
vi playbook.yml
```yaml

Now remove the line with the following:

```yaml
- install_php: true
```yaml

So our playbook should now look like below:

```yaml
---
- name: Build LAMP Servers
  hosts: lamp-servers
  become: true
  vars:
  roles:
    - role: ansible-apache2
    - role: ansible-mysql
  tasks:
```bash

Now we should have everything in place except our servers to provision
which I will not be going into here.

So now to kick off the Ansible playbook to test out we would run the
following:

```bash
ansible-playbook -i environments/dev/inventory playbook.yml --user remote --ask-pass --ask-sudo-pass
```bash

If you notice above we are specifying the user and prompting for
passwords. We could get around this requirement and add our ssh public
key to the remote servers we will be managing (Highly recommend). You
could follow your normal process of accomplishing this or you can check
out the Ansible role called ansible-manage-ssh-keys that we pulled down
in the beginning of this post.

```bash
cd ~/Git_Projects/GitHub/mrlesmithjr/ansible-manage-ssh-keys/
```bash

But for this exercise let's go ahead and add that role to our
requirements.yml file and pull that down to our roles directory as well.

```bash
vi requirements.yml
```yaml

And paste the below at the end of the file:

```yaml
- src: https://github.com/mrlesmithjr/ansible-manage-ssh-keys.git
```yaml

So our requirements.yml should now look like below:

```yaml
---
- src: https://github.com/mrlesmithjr/ansible-apache2.git
- src: https://github.com/mrlesmithjr/ansible-mysql.git
- src: https://github.com/mrlesmithjr/ansible-manage-ssh-keys.git
```bash

Now let's pull down our requirements once again. Remember we will get
errors if the role already exists so you may want to force the install
as previously shown or if you want to ensure that you do not pull any
changes in our upstream repo do not force the install.

```bash
ansible-galaxy install -r requirements.yml

(ansible-1.9.4)remote@ansible-control:~/Git_Projects/Lab/LAMP$ ansible-galaxy install -r requirements.yml
- executing: git clone https://github.com/mrlesmithjr/ansible-apache2.git ansible-apache2
- executing: git archive --prefix=ansible-apache2/ --output=/tmp/tmp53JbAU.tar HEAD
- extracting ansible-apache2 to ./roles/ansible-apache2
- error: the specified role ansible-apache2 appears to already exist. Use --force to replace it.
- ansible-apache2 was NOT installed successfully.
- you can use --ignore-errors to skip failed roles.
(ansible-1.9.4)remote@ansible-control:~/Git_Projects/Lab/LAMP$
```bash

As before we received errors and if you check our roles directory the
new role was not pulled down either.

```bash
(ansible-1.9.4)remote@ansible-control:~/Git_Projects/Lab/LAMP$ ls roles
ansible-apache2  ansible-mysql
(ansible-1.9.4)remote@ansible-control:~/Git_Projects/Lab/LAMP$
```bash

How can we get around this without forcing the install? If you notice in
the messages above there is a reference to using --ignore-errors. We
can leverage that parameter in order to pull down our additional roles
without touching the previously existing roles.

```bash
ansible-galaxy install -r requirements.yml -i

(ansible-1.9.4)remote@ansible-control:~/Git_Projects/Lab/LAMP$ ansible-galaxy install -r requirements.yml -i
- executing: git clone https://github.com/mrlesmithjr/ansible-apache2.git ansible-apache2
- executing: git archive --prefix=ansible-apache2/ --output=/tmp/tmpNuZPUo.tar HEAD
- extracting ansible-apache2 to ./roles/ansible-apache2
- error: the specified role ansible-apache2 appears to already exist. Use --force to replace it.
- ansible-apache2 was NOT installed successfully.
- executing: git clone https://github.com/mrlesmithjr/ansible-mysql.git ansible-mysql
- executing: git archive --prefix=ansible-mysql/ --output=/tmp/tmpLQFR1m.tar HEAD
- extracting ansible-mysql to ./roles/ansible-mysql
- error: the specified role ansible-mysql appears to already exist. Use --force to replace it.
- ansible-mysql was NOT installed successfully.
- executing: git clone https://github.com/mrlesmithjr/ansible-manage-ssh-keys.git ansible-manage-ssh-keys
- executing: git archive --prefix=ansible-manage-ssh-keys/ --output=/tmp/tmp2RVBbl.tar HEAD
- extracting ansible-manage-ssh-keys to ./roles/ansible-manage-ssh-keys
- ansible-manage-ssh-keys was installed successfully
(ansible-1.9.4)remote@ansible-control:~/Git_Projects/Lab/LAMP$
```bash

Now you see that at the end our ansible-manage-ssh-keys role was
successfully installed. And to verify:

```bash
ls roles/

(ansible-1.9.4)remote@ansible-control:~/Git_Projects/Lab/LAMP$ ls roles/
ansible-apache2  ansible-manage-ssh-keys  ansible-mysql
(ansible-1.9.4)remote@ansible-control:~/Git_Projects/Lab/LAMP$
```bash

Now we see our additional role as being installed.

Now we are ready to setup our ssh keys to initiate ssh password-less
sessions to run our Ansible plays against our servers.

```bash
ssh-keygen

(ansible-1.9.4)remote@ansible-control:~/Git_Projects/Lab/LAMP$ ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/home/remote/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/remote/.ssh/id_rsa.
Your public key has been saved in /home/remote/.ssh/id_rsa.pub.
The key fingerprint is:
98:bc:e8:9c:a8:a3:c2:54:eb:2b:b9:9d:33:a1:cb:78 remote@ansible-control
The key's randomart image is:
+--[ RSA 2048]----+
|                 |
|                 |
|                 |
|   . . o         |
|  . . + S        |
| . o . .         |
|o + o .          |
|==EB..           |
|*B=+B            |
+-----------------+
(ansible-1.9.4)remote@ansible-control:~/Git_Projects/Lab/LAMP$
```sql

Now that we have generated our users ssh keys (remote is username in
this case) we are now ready to setup our ability to deploy these keys.

So let's create the directory in which this role will look for our ssh
keys to deploy.

```bash
mkdir ssh_pub_keys
```yaml

Now we need to copy our ssh public key that we just generated and we
will give it a name of the [email protected] to easily identify.

```bash
cp ~/.ssh/id_rsa.pub ssh_pub_keys/$USER@$HOSTNAME.pub
```bash

And if we take a look at our ssh_pub_keys directory:

```bash
ls ssh_pub_keys/

(ansible-1.9.4)remote@ansible-control:~/Git_Projects/Lab/LAMP$ ls ssh_pub_keys/
[email protected]
(ansible-1.9.4)remote@ansible-control:~/Git_Projects/Lab/LAMP$
```bash

Now before we go any further we need to decide whether we will be using
an account that already exists on our remote servers to add our ssh pub
to or if we will be creating a new account for remote provisioning of
our Ansible roles. I prefer to use an account called remote but this can
be any account that you desire which has sudo access and preferably no
password for sudo. **_(DO NOT USE ROOT). _**If you need to setup user
accounts you can leverage the Ansible role called ansible-users that we
pulled down in the beginning of this post. And have a look at that and
leverage the role if desired. For this example I will be leveraging this
role to keep consistency.

So I will add the following additional role to our requirements.yml file
and then install the additional role.

```bash
cd ~/Git_Projects/Lab/LAMP
vi requirements.yml
```yaml

Add the following additional role:

```yaml
- src: https://github.com/mrlesmithjr/ansible-users.git
```yaml

Now our requirements.yml should look like below:

```yaml
---
- src: https://github.com/mrlesmithjr/ansible-apache2.git
- src: https://github.com/mrlesmithjr/ansible-mysql.git
- src: https://github.com/mrlesmithjr/ansible-manage-ssh-keys.git
- src: https://github.com/mrlesmithjr/ansible-users.git
```yaml

And again let's install our requirements. (Remember we will be ignoring
errors (-i) but you can force (-f) the install as well)

```bash
ansible-galaxy install -r requirements.yml -i

(ansible-1.9.4)remote@ansible-control:~/Git_Projects/Lab/LAMP$ ansible-galaxy install -r requirements.yml -i
- executing: git clone https://github.com/mrlesmithjr/ansible-apache2.git ansible-apache2
- executing: git archive --prefix=ansible-apache2/ --output=/tmp/tmp2xUow3.tar HEAD
- extracting ansible-apache2 to ./roles/ansible-apache2
- error: the specified role ansible-apache2 appears to already exist. Use --force to replace it.
- ansible-apache2 was NOT installed successfully.
- executing: git clone https://github.com/mrlesmithjr/ansible-mysql.git ansible-mysql
- executing: git archive --prefix=ansible-mysql/ --output=/tmp/tmpyT61RC.tar HEAD
- extracting ansible-mysql to ./roles/ansible-mysql
- error: the specified role ansible-mysql appears to already exist. Use --force to replace it.
- ansible-mysql was NOT installed successfully.
- executing: git clone https://github.com/mrlesmithjr/ansible-manage-ssh-keys.git ansible-manage-ssh-keys
- executing: git archive --prefix=ansible-manage-ssh-keys/ --output=/tmp/tmpCOJ05g.tar HEAD
- extracting ansible-manage-ssh-keys to ./roles/ansible-manage-ssh-keys
- error: the specified role ansible-manage-ssh-keys appears to already exist. Use --force to replace it.
- ansible-manage-ssh-keys was NOT installed successfully.
- executing: git clone https://github.com/mrlesmithjr/ansible-users.git ansible-users
- executing: git archive --prefix=ansible-users/ --output=/tmp/tmpIy507F.tar HEAD
- extracting ansible-users to ./roles/ansible-users
- ansible-users was installed successfully
```sql

Now that the ansible-users role has been installed we need to take a
look and see what we need to add to create a new user called remote on
our servers which we will attach our ssh keys to and use for Ansible
deployments.
So if you look at `roles/ansible-users/defaults/main.yml`

```yaml
---
## defaults file for ansible-users
create_local_users: true  #defines creating local user accounts on hosts
create_users:  #defines user accounts to setup on hosts....define here or in group_vars/all
  - user: demo_user  #define username
    authorized_keys: ''
    comment: 'Demo user'  #define a comment to associate with the account
    generate_keys: false  #generate ssh keys...true|false
    home: ''  #define a different home directory... ''=/home/username
    pass: demo_password  #define password for account
    setup: false  #true=creates account|false=removes account if exists...true|false
    shell: ''  #define a different shell for the user
    preseed_user: false  #defines if user should be setup as default user during preseed auto-install...Only 1 user can be added...used in tftpserver Ansible role (mrlesmithjr.tftpserver or ansible-tftpserver)
    sudo: false  #define if user should have sudo access...true|false
    system_account: false  #define if account is a system account...true|falseinstall_fail2ban: false
```sql

From the above we will need to define our user(s) to create on our
remote servers. In my case I will be creating a user named remote and
granting sudo access to this account. Now we could easily add our
account information to the above file but rather than doing that we will
create a new group_vars file that will contain our accounts. We can
either define this for the lamp-servers group or the all group. Defining
on lamp-servers will only apply to our lamp-servers whereas if we define
in all, the account(s) will be created on all of our servers and not
just lamp-servers.

So let's create our group_vars/all variable file for our Dev
environment.

```bash
mkdir environments/dev/group_vars/all
vi environments/dev/group_vars/all/accounts.yml
```yaml

And add the following..Modify of course for your environment.

```yaml
---
create_users:  #defines user accounts to setup on hosts....define here or in group_vars/all
  - user: remote  #define username
    authorized_keys: ''
    comment: 'Ansible remote user'  #define a comment to associate with the account
    generate_keys: false  #generate ssh keys...true|false
    home: ''  #define a different home directory... ''=/home/username
    pass: password1  #define password for account
    setup: true  #true=creates account|false=removes account if exists...true|false
    shell: ''  #define a different shell for the user
    preseed_user: false  #defines if user should be setup as default user during preseed auto-install...Only 1 user can be added...used in tftpserver Ansible role (mrlesmithjr.tftpserver or ansible-tftpserver)
    sudo: true  #define if user should have sudo access...true|false
    system_account: false  #define if account is a system account...true|falseinstall_fail2ban: false
```yaml

Now that is set we also need to define our ssh pub key information for
our ansible-manage-ssh-keys role. So if you look at the default vars for
this role.

```yaml
---
## defaults file for ansible-manage-ssh-keys
enable_manage_ssh_keys: false  #defines if remote ssh keys should be managed
manage_ssh_keys:
  - remote_user: demo_user  #define username on remote system to add defined keys to
    present: true  #defines if ssh key should be added or removed
    keys:  #define key(s) to add to remote username
      - ssh_pub_keys/demo_user.pub
      - ssh_pub_keys/demo_user_1.pub
  - remote_user: demo_user2
    present: false
    keys:
      - ssh_pub_keys/demo_user2.pub
```bash

From the above default variables we need to add the info for our
generated keys and associate that with the account (remote) that we will
be creating on our servers. So let's do that by adding this information
to the same file we created above to add our user(s) to. This way we can
keep everything together in an accounts related file and apply security
against it if desired (recommended) using ansible-vault (More on this
another time).

```bash
vi environments/dev/group_vars/all/accounts.yml
```yaml

And modify the above variables to match the below and append them to our
accounts.yml file.

```yaml
enable_manage_ssh_keys: true  #defines if remote ssh keys should be managed
manage_ssh_keys:
  - remote_user: remote  #define username on remote system to add defined keys to
    present: true  #defines if ssh key should be added or removed
    keys:  #define key(s) to add to remote username
      - ssh_pub_keys/[email protected]
```yaml

Now our accounts.yml file should like below:

```yaml
---
create_users:  #defines user accounts to setup on hosts....define here or in group_vars/all
  - user: remote  #define username
    authorized_keys: ''
    comment: 'Ansible remote user'  #define a comment to associate with the account
    generate_keys: false  #generate ssh keys...true|false
    home: ''  #define a different home directory... ''=/home/username
    pass: password1  #define password for account
    setup: true  #true=creates account|false=removes account if exists...true|false
    shell: ''  #define a different shell for the user
    preseed_user: false  #defines if user should be setup as default user during preseed auto-install...Only 1 user can be added...used in tftpserver Ansible role (mrlesmithjr.tftpserver or ansible-tftpserver)
    sudo: true  #define if user should have sudo access...true|false
    system_account: false  #define if account is a system account...true|falseinstall_fail2ban: false
enable_manage_ssh_keys: true  #defines if remote ssh keys should be managed
manage_ssh_keys:
  - remote_user: remote  #define username on remote system to add defined keys to
    present: true  #defines if ssh key should be added or removed
    keys:  #define key(s) to add to remote username
      - ssh_pub_keys/[email protected]
```yaml

Now that we have all of the above taken care of we need to make sure our
playbook has all of the appropriate roles added. And it should now look
like below:

```yaml
---
- name: Build LAMP Servers
  hosts: lamp-servers
  become: true
  vars:
  roles:
    - role: ansible-users
    - role: ansible-manage-ssh-keys
    - role: ansible-apache2
    - role: ansible-mysql
  tasks:
```bash

## Tweaks

Now how about some tweaking to speed things up? Well one thing we can do
is enable fact caching. Ansible by default allows you to either use
Redis or a jsonfile to cache facts that are gathered. I prefer to use
Redis but that is just my preference. Depending on your choice you can
follow the respective section below.

`Redis`:
We need to first install redis and we can do so as below:

```bash
sudo apt-get install redis-server
```bash

If you are using OSX then you can install redis as follows using
[homebrew](http://brew.sh/):

```bash
brew install redis
ln -sfv /usr/local/opt/redis/*.plist ~/Library/LaunchAgents
```bash

Now you can validate that redis is running:
`Ubuntu/Debian`:

```bash
sudo service redis-server status
```bash

`OSX`:

```bash
brew services list | grep redis
```bash

Now let's configure Ansible to leverage redis and we will do that by
editing our ansible.cfg (You can do this globally or per project. We
will be doing it at a project level).

```bash
cd ~/Git_Projects/Lab/LAMP
vi ansible.cfg
```bash

And add the following to the end of the file.

```bash
gathering = smart
fact_caching = redis
fact_caching_timeout = 86400
```bash

And our ansible.cfg file should look like the following now:

```bash
[defaults]
roles_path = ./roles
gathering = smart
fact_caching = redis
fact_caching_timeout = 86400
```bash

Now we need to install the redis python module in each of our Python
Virtual Environments.

`ansible-1.9.4`

```bash
deactivate
workon ansible-1.9.4
pip install redis
```bash

Now let's configure our 2.0 environment.

```bash
deactivate
workon ansible-2.0
pip install redis
```sql

Now we can now leverage Redis for our fact_caching which will speed up
our plays when they start.

## Updating Python Modules

How can we update all of our Python modules for each of our Virtual
Environments? Actually quite easy. Just do the following in each
environment:

```bash
pip freeze | xargs pip install -U
```bash

So we would do the following for our two Ansible Virtual Environments.\
`ansible-1.9.4`

```bash
deactivate
workon ansible-1.9.4
pip freeze | xargs pip install -U
```bash

`ansible-2.0`

```bash
deactivate
workon ansible-2.0
pip freeze | xargs pip install -U
```bash

Now all of our Python modules for each Virtual Environment are
up-to-date.

## Additional Ansible Tools

Below is a list of some additional tools that you may want to checkout
as well.

[Ansible-Toolbox](https://github.com/dellis23/ansible-toolkit)

```bash
pip install ansible-toolbox

And there you have it. This will conclude Part-1 of setting up an Ansible Control Machine. Up next will be actually beginning our preparation of our remote servers leveraging these roles that we just finished preparing.

Feel free to leave feedback as I am interested in hearing from others and hopefully this has been useful.

Enjoy!