Вы находитесь на странице: 1из 57

Ansible Setup and Ad Hoc

Command Lab
In this lab, you set up and configure the Ansible Controller server for
managing remote hosts. You install the required packages on the
Controller server, create a user, and set up SSH private keys. Then
you test connecting to remote hosts. Finally, you run ad hoc
commands to manage the remote hosts.
Goals
 Install Red Hat Ansible Engine
 Create a user on the remote hosts
 Test connectivity to the remote hosts
 Explore ad hoc commands

1. Connect to Environment
1. Set some useful environment variables:
2. [laptop ]$ export GUID=<"GUID from email">
3. [laptop ]$ export MYKEY=<~/.ssh/your_key.pem>
[laptop ]$ export MYUSER=<username-company.com>

Example
[laptop ]$ export GUID=e4gh
[laptop ]$ export MYKEY=~/.ssh/psrivatkey
[laptop ]$ export MYUSER=psrivast-redhat.com

4. Connect to the bastion host with your OPENTLC ID and private


key:
[laptop ]$ ssh -i ${MYKEY}
${MYUSER}@bastion.${GUID}.example.opentlc.com

2. Configure Ansible
Controller
In this section, you set up and configure the bastion host as the
Ansible Controller server.
You create a devops user on the Ansible Controller and generate an
SSH key pair for the user. The devops user is used to run all of the
Ansible CLI commands to manage the remote hosts.

2.1. Install Ansible


1. Install Ansible on the bastion host:
2. [user-company.com@bastion ~]$ sudo -i
[root@bastion ~]# yum install ansible

Sample Output
Loaded plugins: amazon-id, rhui-lb, search-disabled-repos
Package ansible-2.6.2-1.el7.noarch already installed and latest
version
Nothing to do

The ansible package must be installed from a supported repository using yum.

2.2. Create User on Bastion Host and


Generate SSH Key Pair
1. Create a user named devops with the password r3dh4t1!:
2. [root@bastion ~]# useradd devops
[root@bastion ~]# echo 'r3dh4t1!' | passwd --stdin devops

Sample Output
Changing password for user devops.
passwd: all authentication tokens updated successfully.

3. Generate an SSH key pair for the devops user:


4. [root@bastion ~]# su - devops
[devops@bastion ~]$ ssh-keygen -N '' -f ~/.ssh/id_rsa

Sample Output
Generating public/private rsa key pair.
Created directory '/home/devops/.ssh'.
Your identification has been saved in /home/devops/.ssh/id_rsa.
Your public key has been saved in /home/devops/.ssh/id_rsa.pub.
The key fingerprint is:
SHA256:kmAyJJMCordVF51xd4AmkgzDRYa8rcMUjStBHfL7Dr0
devops@bastion.${GUID}.example.opentlc.com
The key's randomart image is:
+---[RSA 2048]----+
|*...o+=X*+.o..o..|
|=+ .+B+= +.o. . |
|o + +..= . o |
| . *..+o. |
| . ++.S |
| ++ |
| ..o |
| o . |
| E |
+----[SHA256]-----+

5. Verify that the SSH key was successfully created:


[devops@bastion ~]$ ls -l ~/.ssh/

Sample Output
total 8
-rw-------. 1 devops devops 1675 Aug 16 00:23 id_rsa
-rw-r--r--. 1 devops devops 421 Aug 16 00:23 id_rsa.pub

3. Set Up Remote Hosts


In this section, you set up the remote hosts. You create
the devops user on all of the remote hosts and copy its public key to
each host.
The root user on the bastion host is already set up to connect to the remote host as ec2-user
lab, you initially use the root user to run Ansible ad hoc commands to set up the remote host

3.1. Explore Environment


1. As the root user, explore the remote hosts:
[root@bastion ~]# ansible all --list-hosts

Sample Output
hosts (5):
frontend1.${GUID}.internal
support1.${GUID}.internal
app1.${GUID}.internal
app2.${GUID}.internal
appdb1.${GUID}.internal

2. Test connectivity to the remote hosts:


[root@bastion ~]# ansible all -m ping

Sample Output
app1.${GUID}.internal | SUCCESS => {
"changed": false,
"ping": "pong"
}
support1.${GUID}.internal | SUCCESS => {
"changed": false,
"ping": "pong"
}
app2.${GUID}.internal | SUCCESS => {
"changed": false,
"ping": "pong"
}
frontend1.${GUID}.internal | SUCCESS => {
"changed": false,
"ping": "pong"
}
appdb1.${GUID}.internal | SUCCESS => {
"changed": false,
"ping": "pong"
}

3.2. Create User and Set Up SSH Keys


for Remote Host
In this section, you create the user and set up the SSH keys for
the devops user on the remote hosts.
1. Create the devops user on the remote hosts using the
Ansible user module:
[root@bastion ~]# ansible all -m user -a "name=devops"

Sample Output
appdb1.${GUID}.internal | SUCCESS => {
"changed": true,
"comment": "",
"create_home": true,
"group": 1001,
"home": "/home/devops",
"name": "devops",
"shell": "/bin/bash",
"state": "present",
"system": false,
"uid": 1001
}
frontend1.${GUID}.internal | SUCCESS => {
"changed": true,
"comment": "",
"create_home": true,
"group": 1001,
"home": "/home/devops",
"name": "devops",
"shell": "/bin/bash",
"state": "present",
"system": false,
"uid": 1001
}

output omitted....

2. Display the SSH public key for the devops user:


[root@bastion ~]# cat /home/devops/.ssh/id_rsa.pub

Sample Output
ssh-rsa AAAABLzz3......lxV1sZld0sGVP
devops@bastion.${GUID}.example.opentlc.com

3. Add the SSH key to the authorized keys for the devops user,
making sure to replace the value of the SSH public key with the
one that you just displayed:
[root@bastion ~]# ansible all -m authorized_key -a "user=devops
state=present key='ssh-rsa AAAAB......3lxV1sZld0sGVP
devops@bastion.${GUID}.example.opentlc.com'"

o The contents of /home/devops/.ssh/id_rsa.pub is used as the value of


the parameter key for the authorized_key Ansible module.
Sample Output
app1.${GUID}.internal | SUCCESS => {
"changed": true,
"comment": null,
"exclusive": false,
"key": "ssh-rsa
AAAAB3NzaC1yc2EAAAADAQABAAABAQDfPKFdlmfxH8+xbVtJdSy1Jhc53lZCyKn
S6NVm2GzobK3T4ZJKm8VBRV2WSOieOoy0cChygqEI7thpIGSJ69n/hwEC8lRbTp
PNJQs7NNf6SrgmhWkep82CQtO8ElkzssEtwNbAXsC7Bzt1OKtdHPGpMJdB7KwGF
D8NarrF2nbRWTWjoSoUhm6WNZ3kjPbXZH5STSjwrNnq0FWj9PpHs3d6dqjBj1X+
pcAuao4/CJ1s++cVcK3s5aHjNGAnzZffILHx8jWPVsPuzECm7pu3R3H6L07B2R/
k9x+eezBMcW/Pyu6M4aGNX4xQPnv2KMtLQTblEcbLzz3lxV1sZld0sGVP
devops@bastion.${GUID}.example.opentlc.com",
"key_options": null,
"keyfile": "/home/devops/.ssh/authorized_keys",
"manage_dir": true,
"path": null,
"state": "present",
"unique": false,
"user": "devops",
"validate_certs": true
}
Output Omited....

4. Configure sudo on the remote host for privileged escalation for


the devops user:
[root@bastion ~]# ansible all -m lineinfile -a "dest=/etc/sudoers
state=present line='devops ALL=(ALL) NOPASSWD: ALL'"

Sample Output
appdb1.${GUID}.internal | SUCCESS => {
"backup": "",
"changed": true,
"msg": "line added"
}
app1.${GUID}.internal | SUCCESS => {
"backup": "",
"changed": true,
"msg": "line added"
}
Output Omitted....

5. Verify the connection to the remote hosts from bastion as


the devops user, starting with the app1 server:
6. [root@bastion ~]# su - devops
7. [devops@bastion ~]$ export GUID=`hostname | awk -F"." '{print
$2}'`
[devops@bastion ~]$ ssh app1.${GUID}.internal

Sample Output
The authenticity of host 'app1.${GUID}.internal (<no hostip for
proxy command>)' can't be established.
ECDSA key fingerprint is
SHA256:VoJ8NXtSBgbB/YK59iA7yzop56MuGSavYg/0prLGEu4.
ECDSA key fingerprint is
MD5:ee:d9:66:8d:8f:f7:19:bf:9d:a5:79:c2:a9:dc:44:24.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added 'app1.${GUID}.internal' (ECDSA) to the
list of known hosts.
Last login: Thu Aug 16 00:54:17 2018

8. Become the root user on the app1 server:


[devops@app1 ~]$ sudo -i

Sample Output
[root@app1 ~]#
9. Repeat the previous two steps for all of the other remote hosts.

4. Explore Ad Hoc
Commands
After you have successfully configured the Ansible Controller and
remote hosts, you can run Ansible ad hoc commands and
playbooks as the devops user from bastion without being prompted for
the password.
In this section, you explore ad hoc commands to manage remote
hosts.
You configure ansible.cfg and the static inventory needed to complete
this lab. You use-u to specify the devops user and --private-key to
specify the private key.
1. Verify connectivity to the remote hosts:
[devops@bastion ~]$ ansible frontends -m ping

Sample Output
frontend1.${GUID}.internal | UNREACHABLE! => {
"changed": false,
"msg": "Failed to connect to the host via ssh: Warning:
Permanently added 'frontend1.${GUID}.example.opentlc.com' (ECDSA)
to the list of known hosts.\r\nno such identity:
/home/devops/.ssh/${GUID}key.pem: No such file or
directory\r\nPermission denied (publickey,gssapi-keyex,gssapi-
with-mic).\r\n",
"unreachable": true
}

oThe command is expected to fail because it is using the


default ansible.cfg and the SSH keys defined in the
default /etc/ansible/hosts static inventory.
2. Create a directory called ansible_implementation as your working
directory for all future labs and an ansible.cfg file with
a [defaults] section for specifying user-specific settings:
3. [devops@bastion ~]$ mkdir ansible_implementation
4. [devops@bastion ~]$ cd ansible_implementation/
5. [devops@bastion ansible_implementation]$ cat << EOF > ansible.cfg
6. [defaults]
7. inventory = /home/devops/ansible_implementation/hosts
8. host_key_checking = False
EOF

9. Verify the contents of the ansible.cfg file:


[devops@bastion ansible_implementation]$ cat ansible.cfg

Sample Output
[defaults]
inventory = /home/devops/ansible_implementation/hosts
host_key_checking = False

10. Create /home/devops/ansible_implementation/hosts as the static


inventory, which contains the hostnames of all of the remote
hosts:
11. [devops@bastion ansible_implementation]$ export
GUID=`hostname | awk -F"." '{print $2}'`
12. [devops@bastion ansible_implementation]$ cat << EOF >
/home/devops/ansible_implementation/hosts
13. frontend1.${GUID}.internal
14. appdb1.${GUID}.internal
15. app1.${GUID}.internal
16. support1.${GUID}.internal
17. app2.${GUID}.internal
EOF

18. Test connectivity again—and this time, expect it to work:


[devops@bastion ansible_implementation]$ ansible
frontend1.${GUID}.internal -m ping -u devops --private-
key=~/.ssh/id_rsa

Sample Output
frontend1.${GUID}.internal | SUCCESS => {
"changed": false,
"ping": "pong"
}

19. Test connectivity to all of the hosts:


[devops@bastion ansible_implementation]$ ansible all -m ping -u
devops --private-key=~/.ssh/id_rsa

Sample Output
app1.${GUID}.internal | SUCCESS => {
"changed": false,
"ping": "pong"
}
support1.${GUID}.internal | SUCCESS => {
"changed": false,
"ping": "pong"
}
Output Omitted....

20. Execute an ad hoc command on localhost to identify the user


account used by Ansible to perform operations on managed
hosts:
[devops@bastion ansible_implementation]$ ansible localhost -m
command -a 'id'

Sample Output
[WARNING]: provided hosts list is empty, only localhost is
available. Note that the implicit localhost does not match 'all'

localhost | SUCCESS | rc=0 >>


uid=1001(devops) gid=1001(devops) groups=1001(devops)
context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023'

o Expect to see that the ad hoc command is performed on the


managed host as thedevops user.
21. Execute an ad hoc command to display the contents of
the /etc/motd file onapp1.${GUID}.internal as the devops user:
[devops@bastion ansible_implementation]$ ansible
app1.${GUID}.internal -m command -a 'cat /etc/motd' -u devops --
private-key=~/.ssh/id_rsa

Sample Output
app1.${GUID}.internal | SUCCESS | rc=0 >>

o Note that the /etc/motd file is currently empty.


22. Execute an ad hoc command using the copy module and
the devops account to change the contents of the /etc/motd file to
include the message "Managed by Ansible" on all of the remote
hosts:
[devops@bastion ansible_implementation]$ ansible all -m copy -a
'content="Managed by Ansible\n" dest=/etc/motd' -u devops --
private-key=~/.ssh/id_rsa

o Expect the ad hoc command to fail due to insufficient


permissions:
Sample Output
app1.${GUID}.internal | FAILED! => {
"changed": false,
"checksum": "4458b979ede3c332f8f2128385df4ba305e58c27",
"msg": "Destination /etc not writable"
}
Output Omitted...

23. Create the /etc/motd file on all of the hosts, but this time,
escalate the root user’s privileges using -b or --become:
[devops@bastion ansible_implementation]$ ansible all -m copy -a
'content="Managed by Ansible\n" dest=/etc/motd' -u devops --
private-key=~/.ssh/id_rsa --become

Sample Output
app1.${GUID}.internal | SUCCESS => {
"changed": true,
"checksum": "4458b979ede3c332f8f2128385df4ba305e58c27",
"dest": "/etc/motd",
"gid": 0,
"group": "root",
"md5sum": "65a4290ee5559756ad04e558b0e0c4e3",
"mode": "0644",
"owner": "root",
"secontext": "system_u:object_r:etc_t:s0",
"size": 19,
"src": "/home/devops/.ansible/tmp/ansible-tmp-1534387341.14-
178337610750037/source",
"state": "file",
"uid": 0
}
Output Omitted...

24. Execute an ad hoc command to verify the changes


to /etc/motd on all of the remote hosts:
[devops@bastion ansible_implementation]$ ansible all -m command -
a 'cat /etc/motd' -u devops --private-key=~/.ssh/id_rsa --become

Sample Output
app1.${GUID}.internal | SUCCESS | rc=0 >>
Managed by Ansible

support1.${GUID}.internal | SUCCESS | rc=0 >>


Managed by Ansible

frontend1.${GUID}.internal | SUCCESS | rc=0 >>


Managed by Ansible

appdb1.${GUID}.internal | SUCCESS | rc=0 >>


Managed by Ansible
app2.${GUID}.internal | SUCCESS | rc=0 >>
Managed by Ansible

5. Evaluate Your Progress


1. Change to your home directory (/home/devops) before cloning the
grading repository:
[devops@bastion ansible_implementation]$ cd ~

2. Grade your work:


3. [devops@bastion ~]$ git clone
https://github.com/prakhar1985/ansible_implementation_grading.git
4. [devops@bastion ~]$ cd ansible_implementation_grading/
5. [devops@bastion ansible_implementation_grading]$ export
GUID=`hostname | awk -F"." '{print $2}'`
[devops@bastion ansible_implementation_grading]$ ansible-playbook
lab-2-grade.yml -e GUID=${GUID}

6. Correct any reported failures.


7. Rerun the script until you see no failures.

Build Version: 1.4R : Last updated 2019-02-22 12:33:16 EST

Static Inventory and


Playbook Lab
In this lab, you set up and configure a static inventory to group
managed remote hosts according to their role. Then you test the
connection to the remote hosts using a host group. Finally, you
write a playbook to set up an Apache web server with static content.
Goals
 Configure a static inventory to add managed hosts in groups
 Write a playbook to check connectivity using a host group
 Set up a static inventory to use inventory variables
 Write a playbook to deploy an Apache web server to the hosts in
the webservers host group
1. Connect to Environment
1. Set some useful environment variables:
2. [laptop ]$ export GUID=<"GUID from email">
3. [laptop ]$ export MYKEY=<~/.ssh/your_key.pem>
[laptop ]$ export MYUSER=<username-company.com>

Example
[laptop ]$ export GUID=e4gh
[laptop ]$ export MYKEY=~/.ssh/psrivatkey
[laptop ]$ export MYUSER=psrivast-redhat.com

4. Connect to the bastion host with your OPENTLC ID and private


key:
[laptop ]$ ssh -i ${MYKEY}
${MYUSER}@bastion.${GUID}.example.opentlc.com

2. Configure Static Inventory


to Add Host Groups
In this section, you configure a static inventory and add managed
hosts to the hosts file, grouped by their roles:

Managed Host Host Group Name Role

frontend1.${GUID}.internal lb Load balancer server

appdb1.${GUID}.internal db Database server

app1.${GUID}.internal webservers Apache web server

app2.${GUID}.internal webservers Apache web server

1. Append the host groups to the end of


the/home/devops/ansible_implementation/hosts static inventory file:
2. [user-company.com@bastion ~]$ sudo -i
3. [root@bastion ~]# su - devops
4. [devops@bastion ~]$ export GUID=`hostname | awk -F"." '{print
$2}'`
5. [devops@bastion ~]$ cd ~/ansible_implementation
6. [devops@bastion ansible_implementation]$ cat << EOF >>
/home/devops/ansible_implementation/hosts
7. [lb]
8. frontend1.${GUID}.internal
9.
10. [webservers]
11. app1.${GUID}.internal
12. app2.${GUID}.internal
13.
14. [db]
15. appdb1.${GUID}.internal
16.
EOF

17. Verify the contents of the hosts file:


[devops@bastion ansible_implementation]$ cat hosts

Sample Output
frontend1.${GUID}.internal
appdb1.${GUID}.internal
app1.${GUID}.internal
support1.${GUID}.internal
app2.${GUID}.internal
[lb]
frontend1.${GUID}.internal

[webservers]
app1.${GUID}.internal
app2.${GUID}.internal

[db]
appdb1.${GUID}.internal

3. Write Playbook to Verify


Connectivity
In this section, you write a playbook that uses the ping module to
verify the connection to the managed hosts in the webservers host
group.
1. As the devops user, write a playbook to check connectivity
to webservers:
2. [devops@bastion ansible_implementation]$ cat << EOF >
check_webservers.yml
3. - hosts: webservers
4. tasks:
5. - name: Check connectivity
6. ping:
EOF

7. Use --syntax-check to verify the syntax of your playbook:


[devops@bastion ansible_implementation]$ ansible-playbook --
syntax-check check_webservers.yml -u devops --private-
key=~/.ssh/id_rsa

Sample Output
playbook: check_webserver.yml

8. Use --check to perform a dry run of the playbook:


[devops@bastion ansible_implementation]$ ansible-playbook --check
check_webservers.yml -u devops --private-key=~/.ssh/id_rsa

Sample Output
PLAY [webservers]
*****************************************************************
*****************************************************

TASK [Gathering Facts]


*****************************************************************
************************************************
ok: [app1.${GUID}.internal]
ok: [app2.${GUID}.internal]

TASK [Check connectivity]


*****************************************************************
*********************************************
ok: [app1.${GUID}.internal]
ok: [app2.${GUID}.internal]

PLAY RECAP
*****************************************************************
************************************************************
app1.${GUID}.internal : ok=2 changed=0
unreachable=0 failed=0
app2.${GUID}.internal : ok=2 changed=0
unreachable=0 failed=0

9. Run the playbook:


[devops@bastion ansible_implementation]$ ansible-playbook
check_webservers.yml -u devops --private-key=~/.ssh/id_rsa

Sample Output
PLAY [webservers]
*****************************************************************
*****************************************************
TASK [Gathering Facts]
*****************************************************************
************************************************
ok: [app1.${GUID}.internal]
ok: [app2.${GUID}.internal]

TASK [Check connectivity]


*****************************************************************
*********************************************
ok: [app1.${GUID}.internal]
ok: [app2.${GUID}.internal]

PLAY RECAP
*****************************************************************
************************************************************
app1.${GUID}.internal : ok=2 changed=0
unreachable=0 failed=0
app2.${GUID}.internal : ok=2 changed=0
unreachable=0 failed=0

o Note that the play in the playbook is running on app1 and app2 as
both of the systems are member of the webservers host group.

4. Set Up Static Inventory to


Use Inventory Variables
In this section, you configure the hosts inventory file to use an
inventory variable so that you do not have to include -u and --private-
key options to specify the remote user and private key.

1. Append the inventory to the hosts file:


2. devops@bastion ansible_implementation]$ cat << EOF >>
/home/devops/ansible_implementation/hosts
3. [webservers:vars]
4. ansible_user = devops
5. ansible_ssh_private_key_file = /home/devops/.ssh/id_rsa
EOF

6. Verify the contents of the hosts file:


[devops@bastion ansible_implementation]$ cat hosts

Sample Output
Output Omitted....
[webservers]
app1.${GUID}.internal
app2.${GUID}.internal

[db]
appdb1.${GUID}.internal
[webservers:vars]
ansible_user = devops
ansible_ssh_private_key_file = /home/devops/.ssh/id_rsa

7. Run the check_webservers.yml playbook again without specifying any


options:
[devops@bastion ansible_implementation]$ ansible-playbook
check_webservers.yml

Sample Output
PLAY [webservers]
*****************************************************************
*****************************************************

TASK [Gathering Facts]


*****************************************************************
************************************************
ok: [app2.${GUID}.internal]
ok: [app1.${GUID}.internal]

TASK [Check connectivity]


*****************************************************************
*********************************************
ok: [app2.${GUID}.internal]
ok: [app1.${GUID}.internal]

PLAY RECAP
*****************************************************************
************************************************************
app1.${GUID}.internal : ok=2 changed=0
unreachable=0 failed=0
app2.${GUID}.internal : ok=2 changed=0
unreachable=0 failed=0

5. Write, Deploy, and Test


Playbook
In this section, you write a playbook to deploy an Apache web
server on the webservershost group using the yum, service,
and copy modules.
1. Write a playbook to deploy the Apache (HTTPD) web server—
but this time, rather than specifying the --become or -b option for
privileged escalation, use become: yes in your playbook:
2. [devops@bastion ansible_implementation]$ cat << EOF >
/home/devops/ansible_implementation/deploy_apache.yml
3. - hosts: webservers
4. become: yes
5. tasks:
6. - name: Install httpd package
7. yum:
8. name: httpd
9. state: latest
10. - name: Enable and start httpd service
11. service:
12. name: httpd
13. state: started
14. enabled: yes
15. - name: Create index.html file for hosting static content
16. copy:
17. content: "Hoorraaayyy!!! My first playbook ran
successfully"
18. dest: /var/www/html/index.html
19. EOF

20. Run your playbook:


[devops@bastion ansible_implementation]$ ansible-playbook
deploy_apache.yml

Sample Output
PLAY [webservers]
*****************************************************************
*****************************************************

TASK [Gathering Facts]


*****************************************************************
************************************************
ok: [app1.${GUID}.internal]
ok: [app2.${GUID}.internal]

TASK [Install httpd package]


*****************************************************************
******************************************
changed: [app1.${GUID}.internal]
changed: [app2.${GUID}.internal]
TASK [Enable and start httpd service]
*****************************************************************
*********************************
changed: [app2.${GUID}.internal]
changed: [app1.${GUID}.internal]

TASK [Create index.html file for hosting static content]


*****************************************************************
**************
changed: [app2.${GUID}.internal]
changed: [app1.${GUID}.internal]

PLAY RECAP
*****************************************************************
************************************************************
app1.${GUID}.internal : ok=4 changed=3
unreachable=0 failed=0
app2.${GUID}.internal : ok=4 changed=3
unreachable=0 failed=0

21. Verify that you are able to access the web page on
the app1 host:
[devops@bastion ansible_implementation]$ curl
http://app1.${GUID}.internal

Sample Output
Hoorraaayyy!!! My first playbook ran successfully

22. Verify that you are able to access the web page on
the app2 host:
[devops@bastion ansible_implementation]$ curl
http://app2.${GUID}.internal

Sample Output
Hoorraaayyy!!! My first playbook ran successfully

6. Evaluate Your Progress


1. Grade your work:
2. [devops@bastion ansible_implementation]$ cd
~/ansible_implementation_grading/
3. [devops@bastion ansible_implementation_grading]$ export
GUID=`hostname | awk -F"." '{print $2}'`
[devops@bastion ansible_implementation_grading]$ ansible-playbook
lab-3.1-grade.yml -e GUID=${GUID}
4. Correct any reported failures.
5. Rerun the script until you see no failures.

7. Clean Up Environment
1. Run a playbook to perform the cleanup:
[devops@bastion ansible_implementation]$ cd
~/ansible_implementation_grading/
[devops@bastion ansible_implementation_grading]$ export
GUID=`hostname | awk -F"." '{print $2}'`
[devops@bastion ansible_implementation_grading]$ ansible-playbook
lab-3.1-cleanup.yml -e GUID=${GUID}

Build Version: 1.4R : Last updated 2019-02-22 12:33:16 EST

Facts Lab
In this lab, you gather Ansible facts from a managed host. Then you
create custom facts and use them in a playbook. Finally, you install
packages based on those custom facts.
Goals
 Work with facts
 Create custom facts
 Verify custom facts
 Use facts to configure the web server hosts

1. Connect to Environment
1. Set some useful environment variables:
2. [laptop ]$ export GUID=<"GUID from email">
3. [laptop ]$ export MYKEY=<~/.ssh/your_key.pem>
[laptop ]$ export MYUSER=<username-company.com>

Example
[laptop ]$ export GUID=e4gh
[laptop ]$ export MYKEY=~/.ssh/psrivatkey
[laptop ]$ export MYUSER=psrivast-redhat.com
4. Connect to the bastion host with your OPENTLC ID and private
key:
[laptop ]$ ssh -i ${MYKEY}
${MYUSER}@bastion.${GUID}.example.opentlc.com

2. Gather Facts
In this section, you gather Ansible facts from a managed host and
examine them.
1. Using the Ansible setup module, run an ad hoc command to
retrieve the facts for all of the servers in the db group:
2. [user-company.com@bastion ~]$ sudo -i
3. [root@bastion ~]# su - devops
4. [devops@bastion ~]$ export GUID=`hostname | awk -F"." '{print
$2}'`
5. [devops@bastion ~]$ cd ~/ansible_implementation
[devops@bastion ansible_implementation]$ ansible db -m setup

Sample Output
appdb1.${GUID}.internal | SUCCESS => {
"ansible_facts": {
"ansible_all_ipv4_addresses": [
"192.199.0.176"
],
"ansible_all_ipv6_addresses": [
"fe80::97:55ff:feb1:12b8"
],
"ansible_apparmor": {
"status": "disabled"
},
"ansible_architecture": "x86_64",
"ansible_bios_date": "08/24/2006",
"ansible_bios_version": "4.2.amazon",
"ansible_cmdline": {
"BOOT_IMAGE": "/boot/vmlinuz-3.10.0-693.el7.x86_64",
"LANG": "en_US.UTF-8",
"console": "tty0",
"crashkernel": "auto",
"net.ifnames": "0",

Output Omitted...

o The output displays all of the facts gathered for appdb1 server in
JSON format.
6. Review the variables displayed.
7. Filter the facts matching the ansible_user expression and append a
wildcard to match all of the facts starting with ansible_user:
[devops@bastion ansible_implementation]$ ansible db -m setup -a
'filter=ansible_user*'

Sample Output
appdb1.${GUID}.internal | SUCCESS => {
"ansible_facts": {
"ansible_user_dir": "/home/devops",
"ansible_user_gecos": "",
"ansible_user_gid": 1001,
"ansible_user_id": "devops",
"ansible_user_shell": "/bin/bash",
"ansible_user_uid": 1001,
"ansible_userspace_architecture": "x86_64",
"ansible_userspace_bits": "64"
},
"changed": false
}

3. Create Custom Facts


In this section, you set custom facts for the managed hosts in
the webservers host group.
1. Create a fact file named custom.fact with the following content:
2. [devops@bastion ansible_implementation]$ cat << EOF > custom.fact
3. [general]
4. package = httpd
5. service = httpd
6. state = started
EOF

oThis defines the package to install and the service to start


on app1 and app2.
7. Create a setup_facts.yml playbook to create
the /etc/ansible/facts.dremote directory and save the custom.fact file
to it:
8. [devops@bastion ansible_implementation]$ cat << EOF >
setup_facts.yml
9. - name: Install remote facts
10. hosts: webservers
11. become: yes
12. vars:
13. remote_dir: /etc/ansible/facts.d
14. facts_file: custom.fact
15. tasks:
16. - name: Create the remote directory
17. file:
18. state: directory
19. recurse: yes
20. path: "{{ remote_dir }}"
21. - name: Install the new facts
22. copy:
23. src: "{{ facts_file }}"
24. dest: "{{ remote_dir }}"
EOF

25. Run the playbook:


[devops@bastion ansible_implementation]$ ansible-playbook
setup_facts.yml

26. Using the setup module, run an ad hoc command to display


only the ansible_localsection, which contains user-defined facts:
[devops@bastion ansible_implementation]$ ansible webservers -m
setup -a 'filter=ansible_local'

Sample Output
app2.${GUID}.internal | SUCCESS => {
"ansible_facts": {
"ansible_local": {
"custom": {
"general": {
"package": "httpd",
"service": "httpd",
"state": "started"
}
}
}
},
"changed": false
}
app1.${GUID}.internal | SUCCESS => {
"ansible_facts": {
"ansible_local": {
"custom": {
"general": {
"package": "httpd",
"service": "httpd",
"state": "started"
}
}
}
},
"changed": false
}

o Expect the custom facts to appear.

4. Use Facts to Configure


Web Servers
In this section, you write a playbook that uses both default and user-
defined facts to configure the webservers host group, and make sure
that all of the tasks are defined.
1. Create the first task, which installs the httpd package, using the
user fact for the name of the package.
2. Create another task that uses the custom fact to start
the httpd service:
3. [devops@bastion ansible_implementation]$ cat << EOF >
setup_facts_httpd.yml
4. - name: Install Apache and starts the service
5. hosts: webservers
6. become: yes
7. tasks:
8. - name: Install the required package
9. yum:
10. name: "{{ ansible_local.custom.general.package }}"
11. state: latest
12.
13. - name: Start the service
14. service:
15. name: "{{ ansible_local.custom.general.service }}"
16. state: "{{ ansible_local.custom.general.state }}"
EOF

17. Run the playbook:


[devops@bastion ansible_implementation]$ ansible-playbook
setup_facts_httpd.yml

Sample Output
PLAY [Install Apache and starts the service]
*****************************************************************
**************************

TASK [Gathering Facts]


*****************************************************************
************************************************
ok: [app2.${GUID}.internal]
ok: [app1.${GUID}.internal]

TASK [Install the required package]


*****************************************************************
***********************************
changed: [app1.${GUID}.internal]
changed: [app2.${GUID}.internal]

TASK [Start the service]


*****************************************************************
**********************************************
changed: [app2.${GUID}.internal]
changed: [app1.${GUID}.internal]

PLAY RECAP
*****************************************************************
************************************************************
app1.${GUID}.internal : ok=3 changed=2
unreachable=0 failed=0
app2.${GUID}.internal : ok=3 changed=2
unreachable=0 failed=0

18. Use an ad hoc command to determine whether


the httpd service is running onwebservers:
[devops@bastion ansible_implementation]$ ansible webservers -m
command -a 'systemctl status httpd'

Sample Output
app2.${GUID}.internal | SUCCESS | rc=0 >>
● httpd.service - The Apache HTTP Server
Loaded: loaded (/usr/lib/systemd/system/httpd.service;
disabled; vendor preset: disabled)
Active: active (running) since Fri 2018-08-17 11:41:58 UTC;
2min 19s ago
Docs: man:httpd(8)
man:apachectl(8)
Main PID: 2536 (httpd)
Status: "Total requests: 0; Current requests/sec: 0; Current
traffic: 0 B/sec"
CGroup: /system.slice/httpd.service
├─2536 /usr/sbin/httpd -DFOREGROUND
├─2537 /usr/sbin/httpd -DFOREGROUND
├─2538 /usr/sbin/httpd -DFOREGROUND
├─2539 /usr/sbin/httpd -DFOREGROUND
├─2540 /usr/sbin/httpd -DFOREGROUND
└─2541 /usr/sbin/httpd -DFOREGROUND

app1.${GUID}.internal | SUCCESS | rc=0 >>


● httpd.service - The Apache HTTP Server
Loaded: loaded (/usr/lib/systemd/system/httpd.service;
disabled; vendor preset: disabled)
Active: active (running) since Fri 2018-08-17 11:41:58 UTC;
2min 19s ago
Docs: man:httpd(8)
man:apachectl(8)
Main PID: 2516 (httpd)
Status: "Total requests: 0; Current requests/sec: 0; Current
traffic: 0 B/sec"
CGroup: /system.slice/httpd.service
├─2516 /usr/sbin/httpd -DFOREGROUND
├─2517 /usr/sbin/httpd -DFOREGROUND
├─2518 /usr/sbin/httpd -DFOREGROUND
├─2519 /usr/sbin/httpd -DFOREGROUND
├─2520 /usr/sbin/httpd -DFOREGROUND
└─2521 /usr/sbin/httpd -DFOREGROUND

5. Evaluate Your Progress


1. Grade your work:
2. [devops@bastion ~]$ cd ~/ansible_implementation_grading/
3. [devops@bastion ansible_implementation_grading]$ export
GUID=`hostname | awk -F"." '{print $2}'`
[devops@bastion ansible_implementation_grading]$ ansible-playbook
lab-3.2-grade.yml -e GUID=${GUID}

4. Correct any reported failures.


5. Rerun the script until you see no failures.

6. Clean Up Environment
1. Run a playbook to perform the cleanup:
2. [devops@bastion ansible_implementation]$ cd
~/ansible_implementation_grading/
3. [devops@bastion ansible_implementation_grading]$ export
GUID=`hostname | awk -F"." '{print $2}'`
[devops@bastion ansible_implementation_grading]$ ansible-playbook
lab-3.2-cleanup.yml -e GUID=${GUID}

Build Version: 1.4R : Last updated 2019-02-22 12:33:16 EST

Variables Lab
In this lab, you define and use variables in a playbook. You create a
playbook that installs the Apache web server and opens the ports
for the service to be reachable. The playbook queries the web
server to ensure that it is up and running.
Goals
 Define variables in a playbook and create tasks that include
defined variables
 Gather facts from a host and create tasks that use the gathered
facts
 Define variables and tasks in separate files and use the files in
playbooks

1. Connect to Environment
1. Set some useful environment variables:
2. [laptop ]$ export GUID=<"GUID from email">
3. [laptop ]$ export MYKEY=<~/.ssh/your_key.pem>
[laptop ]$ export MYUSER=<username-company.com>

Example
[laptop ]$ export GUID=e4gh
[laptop ]$ export MYKEY=~/.ssh/psrivatkey
[laptop ]$ export MYUSER=psrivast-redhat.com

4. Connect to the bastion host with your OPENTLC ID and private


key:
[laptop ]$ ssh -i ${MYKEY}
${MYUSER}@bastion.${GUID}.example.opentlc.com

5. Log in as the devops user and change to the ~/dev-vars-


playbook directory:
6. [user-company.com@bastion ~]$ sudo -i
7. [root@bastion ~]# su - devops
8. [devops@bastion ~]$ mkdir ansible_implementation
9. [devops@bastion ~]$ cd ansible_implementation
[devops@bastion ansible_implementation]$

2. Create Playbook to Set Up


Web Services
In this section, you create a playbook to set up web services on the
web servers.
1. Create the variable_test.yml playbook and define the following
variables in thevars section:
o web_pkg defines the name of the package to install for the web
server
o firewall_pkg defines the name of the firewall package

o web_service defines the name of the web service to manage

o firewall_service defines the name of the firewall service to


manage
o python_pkg defines a package to be installed for the uri module

o rule defines the service to open

2. Create the tasks block and add a first task, which uses
the yum module to install the required packages.
3. Add two more tasks to start and enable
the httpd and firewalld services.
4. Add a task that creates content in /var/www/html/index.html.
5. Add a task that uses the firewalld module to add a rule for the
web service.
Playbook Solution
[devops@bastion ansible_implementation]$ cat << EOF >
variable_test.yml
- name: Install Apache and start the service
hosts: webservers
become: yes
vars:
web_pkg: httpd
firewall_pkg: firewalld
web_service: httpd
firewall_service: firewalld
python_pkg: python-httplib2
rule: http
tasks:
- name: Install the required packages
yum:
name:
- "{{ web_pkg }}"
- "{{ firewall_pkg }}"
- "{{ python_pkg }}"
state: latest
- name: Start and enable the {{ firewall_service }} service
service:
name: "{{ firewall_service }}"
enabled: true
state: started

- name: Start and enable the {{ web_service }} service


service:
name: "{{ web_service }}"
enabled: true
state: started
- name: Create web content to be served
copy:
content: "Example web content"
dest: /var/www/html/index.html
- name: Open the port for {{ rule }}
firewalld:
service: "{{ rule }}"
permanent: true
immediate: true
state: enabled

EOF

6. Check the syntax of the variable_test.yml playbook:


[devops@bastion ansible_implementation]$ ansible-playbook --
syntax-check variable_test.yml

Sample Output
playbook: variable_test.yml

3. Create Playbook for


Smoke Test
In this section, you create a new play that queries the web service
to ensure that everything is configured correctly.
1. Name your playbook webserver_smoketest.yml.
2. Configure it to run only on localhost.
3. Create a task that uses the uri module to check a URL.
4. In this task, check for a status code of 200 to confirm that the
server is running and configured properly.
Playbook Solution
[devops@bastion ansible_implementation]$ export GUID=`hostname | awk
-F"." '{print $2}'`
[devops@bastion ansible_implementation]$ cat << EOF >
webserver_smoketest.yml
- name: Verify the Apache service
hosts: localhost
tasks:
- name: Ensure the webserver is reachable
uri:
url: http://app1.${GUID}.internal
status_code: 200
EOF

4. Run Playbooks
In this section, you run the variable_test.yml playbook to set up the
web services ofapp1 and app2. Then you run
the webserver_smoketest.yml smoke-test playbook to verify that the web
services are running on the correct hosts.
1. Run the variable_test.yml playbook:
[devops@bastion ansible_implementation]$ ansible-playbook
variable_test.yml

Sample Output
PLAY [Install Apache and start the service]
*****************************************************************
**************************************************

TASK [Gathering Facts]


*****************************************************************
*****************************************************************
******
ok: [app1.${GUID}.internal]
ok: [app2.${GUID}.internal]

TASK [Install the required packages]


*****************************************************************
*********************************************************
changed: [app1.${GUID}.internal]
changed: [app2.${GUID}.internal]

TASK [Start and enable the firewalld service]


*****************************************************************
************************************************
changed: [app1.${GUID}.internal]
changed: [app2.${GUID}.internal]
TASK [Start and enable the httpd service]
*****************************************************************
****************************************************
changed: [app1.${GUID}.internal]
changed: [app2.${GUID}.internal]

TASK [Create web content to be served]


*****************************************************************
*******************************************************
changed: [app1.${GUID}.internal]
changed: [app2.${GUID}.internal]

TASK [Open the port for http]


*****************************************************************
****************************************************************
changed: [app1.${GUID}.internal]
changed: [app2.${GUID}.internal]

PLAY RECAP
*****************************************************************
*****************************************************************
******************
app1.${GUID}.internal : ok=6 changed=5
unreachable=0 failed=0
app2.${GUID}.internal : ok=6 changed=5
unreachable=0 failed=0

o Note that Ansible starts by installing the packages, and then


starts and enables the services.
2. Run the webserver_smoketest.yml playbook to make sure that the web
server is reachable:
[devops@bastion ansible_implementation]$ ansible-playbook
webserver_smoketest.yml

Sample Output
PLAY [Verify the Apache service]
*****************************************************************
*************************************************************

TASK [Gathering Facts]


*****************************************************************
*****************************************************************
******
ok: [localhost]
TASK [Ensure the webserver is reachable]
*****************************************************************
*****************************************************
ok: [localhost]

PLAY RECAP
*****************************************************************
*****************************************************************
******************
localhost : ok=2 changed=0 unreachable=0
failed=0

5. Evaluate Your Progress


1. Grade your work:
2. [devops@bastion ansible_implementation]$ cd
~/ansible_implementation_grading
3. [devops@bastion ansible_implementation_grading]$ export
GUID=`hostname | awk -F"." '{print $2}'`
[devops@bastion ansible_implementation_grading]$ ansible-playbook
lab-4.1-grade.yml -e GUID=${GUID}

Sample Output
TASK [Fail if 'Example web content' is not in the page content]
*****************************************************************
******************************
skipping: [localhost]

TASK [Success if 'Example web content' is in the page content]


*****************************************************************
*******************************
ok: [localhost] => {
"msg": "Success: All requirements completed."
}

PLAY RECAP
*****************************************************************
*****************************************************************
******************
localhost : ok=7 changed=4 unreachable=0
failed=0

4. Correct any reported failures.


5. Rerun the script until you see no failures.

6. Clean Up Environment
1. Undo the changes made to webservers:
[devops@bastion ansible_implementation_grading]$ ansible-playbook
lab-4.1-cleanup.yml -e GUID=${GUID}

Build Version: 1.4R : Last updated 2019-02-22 12:33:16 EST

Variable Inclusion Lab


In this lab, you manage inclusions in Ansible Playbooks. You create
a task file, variable file, and playbook. The variable file defines, in
YAML format, a variable used by the playbook. The task file defines
the required tasks and includes variables that are passed later on
as arguments.
Goal
 Create an Ansible Playbook that uses inclusions

1. Connect to Environment
1. Set some useful environment variables:
2. [laptop ]$ export GUID=<"GUID from email">
3. [laptop ]$ export MYKEY=<~/.ssh/your_key.pem>
[laptop ]$ export MYUSER=<username-company.com>

Example
[laptop ]$ export GUID=e4gh
[laptop ]$ export MYKEY=~/.ssh/psrivatkey
[laptop ]$ export MYUSER=psrivast-redhat.com

4. Connect to the bastion host with your OPENTLC ID and private


key:
[laptop ]$ ssh -i ${MYKEY}
${MYUSER}@bastion.${GUID}.example.opentlc.com

5. Log in as the devops user and change to the ~/dev-vars-


playbook directory:
6. [user-company.com@bastion ~]$ sudo -i
7. [root@bastion ~]# su - devops
8. [devops@bastion ~]$ mkdir ansible_implementation
9. [devops@bastion ~]$ cd ansible_implementation
[devops@bastion ansible_implementation]$
2. Create Task File
1. Create a tasks directory under ~/ansible_implementation.
2. In the tasks directory, create the environment.yml task file.
3. In the playbook, define the two tasks that install and start the
web server.
4. Use the package variable for the package name, service for the
service name, andsvc_state for the service state.
Playbook Solution
[devops@bastion ansible_implementation]$ mkdir tasks
[devops@bastion ansible_implementation]$ cd tasks
[devops@bastion tasks]$ cat << EOF > environment.yml
- name: Install the {{ package }} package
yum:
name: "{{ package }}"
state: latest
- name: Start the {{ service }} service
service:
name: "{{ service }}"
state: "{{ svc_state }}"
EOF

5. Change back to the main project directory:


6. [devops@bastion tasks]$ cd ..
[devops@bastion ansible_implementation]$

3. Create Variable File


1. Create and change to the vars directory:
2. [devops@bastion ansible_implementation]$ mkdir vars
3. [devops@bastion ansible_implementation]$ cd vars
[devops@bastion vars]$

4. Create the variables.yml variable file with the following content:


5. [devops@bastion vars]$ cat << EOF > variables.yml
6. firewall_pkg: firewalld
EOF

o The file defines the firewall_pkg variable in YAML format.


7. Change back to the main project directory:
8. [devops@bastion vars]$ cd ..
[devops@bastion ansible_implementation]$

4. Create Main Playbook


In this section, you create and edit the main
playbook, main_playbook.yml, which imports the tasks and variables,
and installs and configures the firewalld service.
1. Create the playbook main_playbook.yml.
2. Add the webservers host group and define a rule variable with a
value of http.
3. Define the first task with the include_vars module and
the variables.yml variable file.
o The include_vars module imports extra variables that are used by
other tasks in the playbook.
4. Define a task that uses the import_tasks module to include the
baseenvironment.yml playbook:
a. Because the three defined variables are used in the base
playbook, but are not defined, include a vars block.
b. Set three variables in the vars section:
 package: httpd

 service: httpd

 svc_state: started

5. Create a task that installs the firewalld package using


the firewall_pkg variable.
6. Create a task that starts the firewalld service.
7. Create a task that adds a firewall rule for the HTTP service using
the rule variable.
8. Add a task that creates the index.html file for the web server using
the copymodule:
. Create the file with the Ansible ansible_fqdn fact, which returns
the fully qualified domain name.
a. Include a time stamp in the file using an Ansible fact.
Playbook Solution
[devops@bastion ansible_implementation]$ cat << EOF >
main_playbook.yml
- hosts: webservers
become: yes
vars:
rule: http
tasks:
- name: Include the variables from the YAML file
include_vars: vars/variables.yml

- name: Include the environment file and set the variables


import_tasks: tasks/environment.yml
vars:
package: httpd
service: httpd
svc_state: started

- name: Install the firewall


yum:
name: "{{ firewall_pkg }}"
state: latest

- name: Start the firewall


service:
name: firewalld
state: started
enabled: true

- name: Open the port for {{ rule }}


firewalld:
service: "{{ rule }}"
immediate: true
permanent: true
state: enabled

- name: Create index.html


copy:
content: "{{ ansible_fqdn }} has been customized using
Ansible on the {{ ansible_date_time.date }}\n"
dest: /var/www/html/index.html
EOF

9. Verify the syntax of the main_playbook.yml playbook:


[devops@bastion ansible_implementation]$ ansible-playbook --
syntax-check main_playbook.yml

Sample Output
playbook: main_playbook.yml
[devops@bastion ansible_implementation]$

5. Run Playbook
1. Run the playbook and examine the output:
[devops@bastion ansible_implementation]$ ansible-playbook
main_playbook.yml

Sample Output
PLAY [webservers]
*****************************************************************
*****************************************************************
***********

TASK [Gathering Facts]


*****************************************************************
*****************************************************************
******
ok: [app1.${GUID}.internal]
ok: [app2.${GUID}.internal]

TASK [Include the variables from the YAML file]


*****************************************************************
**********************************************
ok: [app1.${GUID}.internal]
ok: [app2.${GUID}.internal]

TASK [Install the httpd package]


*****************************************************************
*************************************************************
changed: [app1.${GUID}.internal]
changed: [app2.${GUID}.internal]

TASK [Start the httpd service]


*****************************************************************
***************************************************************
changed: [app1.${GUID}.internal]
changed: [app2.${GUID}.internal]

TASK [Install the firewall]


*****************************************************************
*****************************************************************
*
ok: [app1.${GUID}.internal]
ok: [app2.${GUID}.internal]

TASK [Start the firewall]


*****************************************************************
*****************************************************************
***
ok: [app2.${GUID}.internal]
ok: [app1.${GUID}.internal]
TASK [Open the port for http]
*****************************************************************
****************************************************************
ok: [app2.${GUID}.internal]
ok: [app1.${GUID}.internal]

TASK [Create index.html]


*****************************************************************
*****************************************************************
****
changed: [app1.${GUID}.internal]
changed: [app2.${GUID}.internal]

PLAY RECAP
*****************************************************************
*****************************************************************
******************
app1.${GUID}.internal : ok=8 changed=3
unreachable=0 failed=0
app2.${GUID}.internal : ok=8 changed=3
unreachable=0 failed=0

o Note that Ansible starts by including the environment.yml playbook


and running its tasks, then continues to execute the tasks
defined in the main playbook.
2. Confirm that the app1 web server is reachable from bastion:
3. devops@bastion ansible_implementation]$ export GUID=`hostname |
awk -F"." '{print $2}'`
[devops@bastion ansible_implementation]$ curl
http://app1.${GUID}.internal

Sample Output
ip-192-199-0-96.ec2.internal has been customized using Ansible on
the 2018-08-21

4. Confirm that the app2 web server is reachable from bastion:


[devops@bastion ansible_implementation]$ curl
http://app2.${GUID}.internal

Sample Output
ip-192-199-0-96.ec2.internal has been customized using Ansible on
the 2018-08-21

o You see this output because the index.html file was created.

6. Evaluate Your Progress


1. Grade your work:
2. [devops@bastion ansible_implementation]$ cd
~/ansible_implementation_grading/
3. [devops@bastion ansible_implementation_grading]$ export
GUID=`hostname | awk -F"." '{print $2}'`
[devops@bastion ansible_implementation_grading]$ ansible-playbook
lab-4.2-grade.yml -e GUID=${GUID}

4. Correct any reported failures.


5. Rerun the script until you have no failures.

7. Clean Up Environment
1. Run a playbook to clean up the lab environment:
2. [devops@bastion ansible_implementation_grading]$ export
GUID=`hostname | awk -F"." '{print $2}'`
[devops@bastion ansible_implementation_grading]$ ansible-playbook
lab-4.2-cleanup.yml -e GUID=${GUID}

Build Version: 1.4R : Last updated 2019-02-22 12:33:16 EST

Roles Lab
In this lab, you create Ansible roles that use variables, files,
templates, tasks, and handlers to deploy a network service and
enable a working firewall. You then use Ansible Galaxy to initialize a
new Ansible role, and download and install an existing role.
Goals
 Create Ansible roles to deploy a network service and enable a
working firewall
 Use Ansible Galaxy to initialize, download, and install roles

1. Connect to Environment
1. Set some useful environment variables:
2. [laptop ]$ export GUID=<"GUID from email">
3. [laptop ]$ export MYKEY=<~/.ssh/your_key.pem>
[laptop ]$ export MYUSER=<username-company.com>
Example
[laptop ]$ export GUID=e4gh
[laptop ]$ export MYKEY=~/.ssh/psrivatkey
[laptop ]$ export MYUSER=psrivast-redhat.com

4. Connect to the bastion host with your OPENTLC ID and private


key:
[laptop ]$ ssh -i ${MYKEY}
${MYUSER}@bastion.${GUID}.example.opentlc.com

5. Log in as the devops user:


6. [user-company.com@bastion ~]$ sudo -i
[root@bastion ~]# su - devops

7. Run the lab-5.1-setup.yml playbook to set up the lab environment:


8. [devops@bastion ~]$ cd ~/ansible_implementation_grading/
[devops@bastion ansible_implementation_grading]$ ansible-playbook
lab-5.1-setup.yml

9. Change to the ansible_implementation directory:


[devops@bastion ansible_implementation_grading]$ cd
~/ansible_implementation

2. Create Roles
In this section, you create roles to deploy the web application. You
create a role to set up the Apache web server. Then you create a
role to install mariadb and use a database backup file to populate the
database. Lastly, you create a role for setting up a HAProxy load
balancer for high availability for your web application.

2.1. Create Role to Set Up Web


Services
In this section, you create a role to set up httpd services.
1. Create a role called app-tier using the ansible-galaxy command.
2. Add a task to install and enable firewalld.
3. Add a task to install and start httpd.
4. Add a task to create a custom vhost.conf configuration file under
the/etc/httpd/conf.d/ directory.
o The vhost.conf.j2 template is already created to help you with
this step.
5. Add a task to create the /var/www/vhost/ document root directory.
6. Add a task to create an index.php file in the document root
directory using index.j2as the template.
7. Add a task to open the firewall ports as per the requirements.
8. Enable SELinux so that the Apache back-end server can
connect to the database:
o httpd_can_network_connect_db
o httpd_can_network_connect

9. Create the vars/main.yml file under the app-tier role directory that
contains definitions for all of the variables defined in tasks.
10. Create the file handlers/main.yml under the app-tier role directory
that contains a handler to restart services if needed.
Playbook Solution
[devops@bastion ansible_implementation]$ mkdir roles/
[devops@bastion ansible_implementation]$ ansible-galaxy init
roles/app-tier
[devops@bastion ansible_implementation]$ cat << EOF > roles/app-
tier/tasks/main.yml
---
# tasks file for roles/app-tier
# Installation of packages based on inventory groupss
- name: Install Firewalld
yum:
name: firewalld
state: latest

- name: Start firewalld service


service:
name: firewalld
state: started
enabled: true

- name: Install httpd


yum:
name: "{{ item }}"
state: latest
with_items:
- "{{ httpd_pkg }}"

- name: Start httpd


service:
name: "{{ httpd_srv }}"
enabled: true
state: started

- name: Copy vhost template file


template:
src: vhost.conf.j2
dest: /etc/httpd/conf.d/vhost.conf
notify:
- restart_httpd

- name: Create Document Root


file:
path: /var/www/vhost/
state: directory

- name: Copy index.j2 file


template:
src: index.j2
dest: /var/www/vhost/index.php
mode: 0644
owner: apache
group: apache

- name: Open httpd port


firewalld:
service: http
state: enabled
immediate: true
permanent: true

- name: enable selinux boolean


seboolean:
name: "{{ item }}"
state: yes
persistent: yes
loop:
- httpd_can_network_connect_db
- httpd_can_network_connect

EOF

[devops@bastion ansible_implementation]$ cat << EOF > roles/app-


tier/handlers/main.yml
---
# handlers file for roles/app-tier

- name: restart_httpd
service:
name: "{{ httpd_srv }}"
state: restarted

EOF

[devops@bastion ansible_implementation]$ cat << EOF > roles/app-


tier/vars/main.yml
---
# vars file for roles/app-tier

db:
user: root
database: userdb
password: redhat
httpd_pkg:
- httpd
- php
- php-mysql
httpd_srv: httpd
db_srv: mariadb
EOF

[devops@bastion ansible_implementation]$ cp ~/roles-setup-


files/index.j2 roles/app-tier/templates/
[devops@bastion ansible_implementation]$ cp ~/roles-setup-
files/vhost.conf.j2 roles/app-tier/templates/

2.2. Create Role to Set Up Database


In this section, you create a role to install mariadb services and
restore the backup file.
1. Create a role called db-tier using the ansible-galaxy command.
2. Add tasks to your playbook to install and enable
the mariadb service, and startfirewalld, in a similar manner as the
previous exercise.
3. Open firewall ports as per the requirements.
4. Add tasks to check if the mariadb root password is set and set a
password as specified in playbook variables.
5. Add a task to ensure that users have the appropriate privileges
on the database.
6. Add a task to copy the userdb.backup database backup file to the
server.
7. Add a task to restore the userdb.backup backup file for mariadb data.
8. Create a vars/main.yml file under the db-tier role that defines values
for all of the variables defined in the tasks, including these
values for the database:
o user: root

o password: redhat

o database: userdb

o backup file name: userdb.backup

Playbook Solution
[devops@bastion ansible_implementation]$ ansible-galaxy init
roles/db-tier
[devops@bastion ansible_implementation]$ cp ~/roles-setup-
files/userdb.backup roles/db-tier/files/
[devops@bastion ansible_implementation]$ cat << EOF > roles/db-
tier/tasks/main.yml
---
# tasks file for roles/db-tier
- name: Install mysql
yum:
name: "{{ item }}"
state: latest
loop:
- "{{ db_pkg }}"

- name: Start mysql


service:
name: "{{ db_srv }}"
enabled: true
state: started

- name: Start firewalld


service:
name: firewalld
state: started
enabled: true

- name: Open mysql port


firewalld:
service: mysql
state: enabled
immediate: true
permanent: true

- name: Check if root password is set


shell: >
mysqladmin -u root status
changed_when: false
failed_when: false
register: root_pwd_check

- name: Setting up mariadb password


mysql_user:
name: "{{ db['user'] }}"
password: "{{ db['password'] }}"
when: root_pwd_check.rc == 0

- name: DB users have privileges on all databases


mysql_user:
name: "{{ db['user']}}"
priv: "*.*:ALL"
append_privs: yes
password: "{{ db['password']}}"
login_password: "{{ db['password']}}"
host: "{{ item }}"
loop:
- "{{ inventory_hostname }}"
- '%'

- name: Copy database dump file


copy:
src: "{{ db['backupfile']}}"
dest: /tmp

- name: Restore database


mysql_db:
name: "{{ db['database'] }}"
state: import
target: "/tmp/{{ db['backupfile'] }}"
login_password: "{{ db['password']}}"
EOF

[devops@bastion ansible_implementation]$ cat << EOF > roles/db-


tier/vars/main.yml
---
# vars file for roles/db-tier
db_pkg:
- mariadb
- mariadb-server
- MySQL-python
- firewalld
db_srv: mariadb
db:
user: root
database: userdb
password: redhat
backupfile: userdb.backup
EOF

2.3. Create Role to Set Up Load


Balancer
In this section, you create a role to install HAProxy services and use
the webservers host group as the back end.
1. Create a role called lb-tier using the ansible-galaxy command.
2. Add tasks to install and start the firewall, then start HAProxy.
3. Add a task to copy an HAProxy template to the server, using
the haproxy.j2 file as the template.
4. Add a task to open the required HAProxy ports.
5. Create a vars/main.yml file under the lb-tier role directory that
contains definitions for the variables defined in the tasks.
6. Create the handlers/main.yml file under the lb-tier role directory that
contains a handler to restart services if needed.
Playbook Solution
[devops@bastion ansible_implementation]$ ansible-galaxy init
roles/lb-tier
[devops@bastion ansible_implementation]$ cp ~/roles-setup-
files/haproxy.j2 roles/lb-tier/templates/
[devops@bastion ansible_implementation]$ cat << EOF > roles/lb-
tier/tasks/main.yml
---
# tasks file for roles/lb-tier
- name: Install Firewalld
yum:
name: firewalld
state: latest

- name: Start firewalld service


service:
name: firewalld
state: started
enabled: true

- name: Install haproxy


yum:
name: "{{ item }}"
state: latest
loop:
- "{{ haproxy_pkg }}"
- name: Start haproxy
service:
name: "{{ haproxy_srv }}"
enabled: true
state: started

- name: Copy haproxy template


template:
src: haproxy.j2
dest: /etc/haproxy/haproxy.cfg
notify:
- restart_haproxy

- name: Open haproxy port


firewalld:
service: http
state: enabled
immediate: true
permanent: true

- name: Open haproxy statistics port


firewalld:
port: 5000/tcp
state: enabled
immediate: true
permanent: true
EOF

[devops@bastion ansible_implementation]$ cat << EOF > roles/lb-


tier/handlers/main.yml
# handlers file for roles/lb-tier
- name: restart_haproxy
service:
name: "{{ haproxy_srv }}"
enabled: true
state: restarted
EOF

[devops@bastion ansible_implementation]$ cat << EOF > roles/lb-


tier/vars/main.yml
---
# vars file for roles/lb-tier
haproxy_pkg:
- haproxy
- firewalld
haproxy_srv: haproxy
EOF

3. Create and Execute Main


Playbook
In this section, you create and execute a main playbook to call all of
the roles.
1. Create the main playbook to invoke the roles as follows:
o Execute the lb-tier role on the lb host group servers.

o Execute the db-tier role on the db host group servers.

o Execute the app-tier role on the webservers host group servers.

Playbook Solution
[devops@bastion ansible_implementation]$ cat << EOF > webapp-
main.yml
- hosts: webservers
become: yes
roles:
- app-tier

- hosts: db
become: yes
roles:
- db-tier

- hosts: lb
become: yes
roles:
- lb-tier
EOF

2. Execute the main playbook:


[devops@bastion ansible_implementation]$ ansible-playbook webapp-
main.yml

Sample Output
Output Ommitted....
PLAY RECAP
*****************************************************************
*****************************************************************
******************
app1.3fd5.internal : ok=10 changed=5 unreachable=0
failed=0
app2.3fd5.internal : ok=10 changed=5 unreachable=0
failed=0
appdb1.3fd5.internal : ok=9 changed=4 unreachable=0
failed=0
frontend1.3fd5.internal : ok=9 changed=4 unreachable=0
failed=0

4. Test Playbook
1. Open a web browser window and enter
thehttp://frontend1.${GUID}.example.opentlc.com/ URL.
2. When the web page prompts you for the username, enter kiosk.

Sample Output
kiosk redhat /bin/bash /home/kiosk

5. Evaluate Your Progress


1. Grade your work:
2. [devops@bastion ansible_implementation]$ cd
~/ansible_implementation_grading
3. [devops@bastion ansible_implementation_grading]$ export
GUID=`hostname | awk -F"." '{print $2}'`
[devops@bastion ansible_implementation_grading]$ ansible-playbook
lab-5.1-grade.yml -e GUID=${GUID}

Sample Output
Output Omitted...

TASK [Check that "roles" dir is present.]


*****************************************************************
****************************************************
ok: [localhost]

TASK [Fail if "roles" directory is not present]


*****************************************************************
**********************************************
skipping: [localhost]

TASK [Success if roles directory is present]


*****************************************************************
*************************************************
ok: [localhost] => {
"msg": "roles directory is present"
}
TASK [Check that "roles/lb-tier" dir is present.]
*****************************************************************
********************************************
ok: [localhost]

TASK [Fail if "roles/lb-tier" directory is not present]


*****************************************************************
**************************************
skipping: [localhost]

TASK [Success if roles directory is present]


*****************************************************************
*************************************************
ok: [localhost] => {
"msg": "roles/lb-tier directory is present"
}

TASK [Check that "roles/db-tier" dir is present.]


*****************************************************************
********************************************
ok: [localhost]

TASK [Fail if "roles/db-tier" directory is not present]


*****************************************************************
**************************************
skipping: [localhost]

TASK [Success if roles directory is present]


*****************************************************************
*************************************************
ok: [localhost] => {
"msg": "roles/db-tier directory is present"
}

TASK [Check that "roles/app-tier" dir is present.]


*****************************************************************
*******************************************
ok: [localhost]

TASK [Fail if "roles/app-tier" directory is not present]


*****************************************************************
*************************************
skipping: [localhost]

TASK [Success if roles directory is present]


*****************************************************************
*************************************************
ok: [localhost] => {
"msg": "roles/app-tier directory is present"
}
TASK [Run a Curl Test against Frontend]
*****************************************************************
******************************************************
ok: [localhost] => (item=frontend1.3fd5.internal)

TASK [Fail if 'Enter User Name' is not in the page content]


*****************************************************************
**********************************
skipping: [localhost]

TASK [Success if 'Enter User Name' is in the page content]


*****************************************************************
***********************************
ok: [localhost] => {
"msg": "Success: All requirements completed."

Output Omitted...

4. Correct any reported failures.


5. Rerun the script until you see no failures.

Build Version: 1.4R : Last updated 2019-02-22 12:33:16 EST

Ansible Vault Lab


In this lab, you explore using encryption and decryption of files.
Then you use an encrypted file with an Ansible Playbook to store
usernames and passwords.
Goals
 Create and edit an encrypted file
 View the contents of an encrypted file
 Change the password of an encrypted file
 Encrypt and decrypt an existing file
 Define playbook variables in an encrypted file
 Create a playbook that uses the encrypted variables file
 Run a playbook using an encrypted file

1. Connect to Environment
1. Set some useful environment variables:
2. [laptop ]$ export GUID=<"GUID from email">
3. [laptop ]$ export MYKEY=<~/.ssh/your_key.pem>
[laptop ]$ export MYUSER=<username-company.com>

Example
[laptop ]$ export GUID=e4gh
[laptop ]$ export MYKEY=~/.ssh/psrivatkey
[laptop ]$ export MYUSER=psrivast-redhat.com

4. Connect to the bastion host with your OPENTLC ID and private


key:
[laptop ]$ ssh -i ${MYKEY}
${MYUSER}@bastion.${GUID}.example.opentlc.com

5. Change to the ansible_implementation directory:


6. [devops@bastion ~]$ cd ~/ansible_implementation
[devops@bastion ansible_implementation]$

2. Manage Encrypted Files


In this exercise, you create and edit an encrypted file and change
the password on an existing encrypted file. You also encrypt and
decrypt an existing file.

2.1. Create and View Encrypted File


1. Create an encrypted file called super-
secret.yml underansible_implementation, entering redhat as the vault
password when prompted:
[devops@bastion ansible_implementation]$ ansible-vault create
super-secret.yml

Sample Output
New Vault password: redhat
Confirm New Vault password: redhat

2. In the editor that is launched, add the following to the file:


This is encrypted.

3. Save the file and exit the editor.


4. Attempt to view the contents of the encrypted super-secret.yml file:
[devops@bastion ansible_implementation]$ cat super-secret.yml
Sample Output
$ANSIBLE_VAULT;1.1;AES256
30353232636462623438613666393263393238613363333735626661646265376
566653765633565
3663386561393538333864306136316265636632386535330a653764616133343
630303633323831
33653136313933636633623431646634636661333762393764396135333236316
338656338383933
3635646662316335370a363264366138333434626261363465636331333539323
734643363326138
34626565353831666333653139323965376335633132313162613838613561396
462323037313132
3264386531353862396233323963613139343635323532346538

o Because super-secret.yml is encrypted, you cannot view the


contents in plain text.
o The default cipher (AES) used to encrypt the file is based on a
shared secret.
5. View the content of the encrypted file, entering redhat as the vault
password when prompted:
[devops@bastion ansible_implementation]$ ansible-vault view
super-secret.yml

Sample Output
Vault password: redhat
This is encrypted.

2.2. Edit and View Encrypted File


In this section, you add content to super-secret.yml and then view the
file.
1. Edit super-secret.yml, specifying redhat as the vault password when
prompted:
[devops@bastion ansible_implementation]$ ansible-vault edit
super-secret.yml

Sample Output
Vault password: redhat

2. Add the following to the end of the file:


This is also encrypted.

3. Save the file and exit the editor.


4. View the content of super-secret.yml, using redhat as the vault
password:
[devops@bastion ansible_implementation]$ ansible-vault view
super-secret.yml

Sample Output
Vault password: redhat
This is encrypted.
This is also encrypted.

2.3. Change Encrypted File Password


1. Change the vault password of the encrypted super-secret.yml file
from redhat toansible:
[devops@bastion ansible_implementation]$ ansible-vault rekey
super-secret.yml

Sample Output
Vault password: redhat
New Vault password: ansible
Confirm New Vault password: ansible
Rekey successful

2.4. Decrypt and Encrypt Encrypted File


1. Decrypt the encrypted super-secret.yml file and save the file assuper-
secret-decrypted.yml, using the ansible-vault decryptsubcommand with
the --output option and ansible as the vault password:
[devops@bastion ansible_implementation]$ ansible-vault decrypt
super-secret.yml --output=super-secret-decrypted.yml

Sample Output
Vault password: ansible
Decryption successful

2. View the contents of the super-secret-decrypted.yml file to verify that it


is decrypted:
[devops@bastion ansible_implementation]$ cat super-secret-
decrypted.yml

Sample Output
This is encrypted.
This is also encrypted.
3. Encrypt the super-secret-decrypted.yml file and save the file aspasswd-
encrypted.yml, this time entering redhat as the vault password:
[devops@bastion ansible_implementation]$ ansible-vault encrypt
super-secret-decrypted.yml --output=super-secret-encrypted.yml

Sample Output
New Vault password: redhat
Confirm New Vault password: redhat
Encryption successful

3. Use Ansible Vault


In this section, you use Ansible Vault to encrypt a local file
containing passwords and use the encrypted version in a playbook
to create users on thefrontend1.${GUID}.internal remote system.

3.1. Create Encrypted Variable File


In this exercise, you create an encrypted file called secret.yml in
theansible_implementation directory. This file defines the password
variables and stores the passwords to be used in the playbook. You
use an associative array variable callednewusers to define two users
and passwords with the name variable
as ansibleuser1and ansibleuser2 and the pw variable as redhat and Re4H1T,
respectively. You set the vault password to redhat.
1. Make sure that you are in the ansible_implementation directory:
[devops@bastion ~]$ cd ~/ansible_implementation

2. Create an encrypted file called secret.yml in ansible_implementation,


providing the password redhat for the vault:
[devops@bastion ansible_implementation]$ ansible-vault create
secret.yml

Sample Output
New Vault password: redhat
Confirm New Vault password: redhat

o This opens a file in the default editor, vim.


3. Add an associative array variable called newusers, containing
key/value pairs for the user names and passwords:
4. newusers:
5. - name: ansibleuser1
6. pw: redhat
7. - name: ansibleuser2
pw: Re4H1T

The password is stored as plain text in the pw variable.


o

8. Save the file and exit the editor.

3.2. Create Playbook That Uses


Encrypted Variable File
In this exercise, you create a playbook that uses the variables
defined in the secret.ymlencrypted file. You name the
playbook create_users.yml and create it under
theansible_implementation directory.
You configure the playbook to use the lb host group defined by the
lab setup script in the inventory file. Then you run this playbook as
the devops user on the remote managed host and configure the
playbook to create users based on the newusers associative array.
This creates the ansibleuser1 and ansibleuser2 users on the hosts in
the lb host group.
1. Create an Ansible Playbook in ansible_implementation/create_users.yml :
2. [devops@bastion ansible_implementation]$ cat << EOF >
create_users.yml
3. ---
4. - name: create user accounts for all our servers
5. hosts: lb
6. become: True
7. remote_user: devops
8. vars_files:
9. - secret.yml
10. tasks:
11. - name: Creating users from secret.yml
12. user:
13. name: "{{ item.name }}"
14. password: "{{ item.pw | password_hash('sha512') }}"
15. with_items: "{{ newusers }}"
EOF

o The password is converted into a password hash that uses


the password_hashhashing filters and sha512 algorithm.
o You use the user module and pass this hashed password as an
argument, as shown in this simplified example:
o user:
o name: user1
password: "{{ 'passwordsaresecret' | password_hash('sha512')
}}"

16. Perform a syntax check of create_users.yml usingansible-playbook --


syntax-check, and include the --ask-vault-pass option to prompt for the
vault password set on secret.yml:
[devops@bastion ansible_implementation]$ ansible-playbook --
syntax-check --ask-vault-pass create_users.yml

Sample Output
Vault password: redhat

playbook: create_users.yml

17. Resolve any syntax errors before continuing.


18. Create a password file called vault-pass with redhat as the
contents and set the permissions of the file to 0600:
19. [devops@bastion ansible_implementation]$ echo 'redhat' >
vault-pass
[devops@bastion ansible_implementation]$ chmod 0600 vault-pass

o This file is used during playbook execution rather than


prompting for a password.

3.3. Execute Playbook With Encrypted


Variable File
In this section, you execute the Ansible Playbook, using the vault
password file to create theansibleuser1 and ansibleuser2 users on a
remote system. The usernames and passwords are stored as
variables in the encrypted secret.yml file. You then connect
tofrontend1.${GUID}.internal via SSH to verify that the playbook
executed properly and created both users.
1. Execute the Ansible Playbook, using vault-pass as the vault
password:
[devops@bastion ansible_implementation]$ ansible-playbook --
vault-password-file=vault-pass create_users.yml

Sample Output
PLAY [create user accounts for all our servers]
********************************
TASK [setup]
*****************************************************************
**
ok: [frontend1.${GUID}.internal]

TASK [Creating users from secret.yml]


******************************************
changed: [frontend1.${GUID}.internal] => (item={u'name':
u'ansibleuser1', u'pw': u'redhat'})
changed: [frontend1.${GUID}.internal] => (item={u'name':
u'ansibleuser2', u'pw': u'Re4H1T'})

PLAY RECAP
*****************************************************************
****
frontend1.${GUID}.internal : ok=2 changed=1
unreachable=0 failed=0

2. Connect to frontend1.${GUID}.internal via SSH first as ansibleuser1 and


then as ansibleuser2 to verify that the users were created.
o For the ansibleuser1 user, use redhat as the password. For
theansibleuser2 user, use Red4H1T as the password.

4. Evaluate Your Progress


1. Grade your work:
2. [devops@bastion ansible_implementation]$ cd
~/ansible_implementation_grading
3. [devops@bastion ansible_implementation_grading]$ export
GUID=`hostname | awk -F"." '{print $2}'`
[devops@bastion ansible_implementation_grading]$ ansible-playbook
lab-6.1-grade.yml -e GUID=${GUID}

4. Correct any reported failures.


5. Rerun the script until you see no failures.

Build Version: 1.4R : Last updated 2019-02-22 12:33:16 EST

Вам также может понравиться