Read this in other languages:
English, 日本語, Français.
Previous exercises showed you the basics of Ansible playbooks. In the next few exercises, we are going to teach some more advanced ansible skills that will add flexibility and power to your automation.
Ansible exists to make tasks simple and repeatable. We also know that not all systems are exactly alike and often require some slight change to the way an Ansible playbook is run. Enter variables.
Variables are how we deal with differences between your systems, allowing you to account for a change in port, IP address or directory, etc..
Loops enable us to repeat the same task over and over again. For example, lets say you want to start multiple services, install several features, or create multiple directories. By using an ansible loop, you can do that in a single task.
Handlers are the way in which we restart services. Did you just deploy a new config file, install a new package? If so, you may need to restart a service for those changes to take effect. We do that with a handler.
For a full understanding of variables, loops, and handlers; check out our Ansible documentation on these subjects. Ansible Variables Ansible Loops Ansible Handlers
To begin, we are going to create a new playbook, but it should look very familiar to the one you created in exercise 3
Within Visual Studio Code, create a new directory in your git repo and create a site.yml file.
In the Explorer accordion you should have a WORKSHOP_PROJECT section where
you previously made iis_basic
.
Hover over the WORKSHOP_PROJECT section and click the New Folder button
Type iis_advanced
and hit enter. Now, click that folder so it is
selected.
Right-click the iis_advanced
folder and select New File.
Type site.yml
and hit enter.
You should now have an editor open in the right pane that can be used for creating your playbook.
Add a play definition and some variables to your playbook. These include addtional packages your playbook will install on your web servers, plus some web server specific configurations.
---
- name: This is a play within a playbook
hosts: windows
vars:
iis_sites:
- name: 'Ansible Playbook Test'
port: '8080'
path: 'C:\sites\playbooktest'
- name: 'Ansible Playbook Test 2'
port: '8081'
path: 'C:\sites\playbooktest2'
iis_test_message: "Hello World! My test IIS Server"
Add a new task called install IIS. After writing the playbook, click
File
> Save
to save your changes.
tasks:
- name: Install IIS
win_feature:
name: Web-Server
state: present
- name: Create site directory structure
win_file:
path: "{{ item.path }}"
state: directory
with_items: "{{ iis_sites }}"
- name: Create IIS site
win_iis_website:
name: "{{ item.name }}"
state: started
port: "{{ item.port }}"
physical_path: "{{ item.path }}"
with_items: "{{ iis_sites }}"
notify: restart iis service
Note
What is happening here!?
vars:
You’ve told Ansible the next thing it sees will be a variable name
iis_sites
You are defining a list-type variable called iis_sites. What follows is a list of each site with it’s related variables
win_file:
This module is used to create, modify, delete files, directories, and symlinks.
{{ item }}
You are telling Ansible that this will expand into a list item. Each item has several variables likename
,port
, andpath
.
with_items: "{{ iis_sites }}
This is your loop which is instructing Ansible to perform this task on everyitem
iniis_sites
notify: restart iis service
This statement is ahandler
, so we’ll come back to it in Section 3.
After that, you will define a task to start the IIS service.
Create a templates
directory in your project directory and create a
template as follows:
Ensure your iis_advanced folder is highlighted and then hover over the WORKSHOP_PROJECT section and click the New Folder button
Type templates
and hit enter. The right-click the templates folder and click the New File button.
Type index.html.j2
and hit enter.
You should now have an editor open in the right pane that can be used for creating your template. Enter the following details:
<html>
<body>
<p align=center><img src='http://docs.ansible.com/images/logo.png' align=center>
<h1 align=center>{{ ansible_hostname }} --- {{ iis_test_message }}</h1>
</body>
</html>
Edit back your playbook, site.yml
, by opening your firewall ports and
writing the template. Use single quotes for win_template
in order to
not escape the forward slash.
- name: Open port for site on the firewall
win_firewall_rule:
name: "iisport{{ item.port }}"
enable: yes
state: present
localport: "{{ item.port }}"
action: Allow
direction: In
protocol: Tcp
with_items: "{{ iis_sites }}"
- name: Template simple web site to iis_site_path as index.html
win_template:
src: 'index.html.j2'
dest: '{{ item.path }}\index.html'
with_items: "{{ iis_sites }}"
- name: Show website addresses
debug:
msg: "{{ item }}"
loop:
- http://{{ ansible_host }}:8080
- http://{{ ansible_host }}:8081
Note
So… what did I just write?
win_firewall_rule:
This module is used to create, modify, and update firewall rules. Note in the case of AWS there are also security group rules which may impact communication. We’ve opened these for the ports in this example.
win_template:
This module specifies that a jinja2 template is being used and deployed.used in Ansible to transform data inside a template expression, i.e. filters.
debug:
Again, like in theiis_basic
playbook, this task displays the URLs to access the sites we are creating for this exercise
There are any number of reasons we often need to restart a service/process including the deployment of a configuration file, installing a new package, etc. There are really two parts to this Section; adding a handler to the playbook and calling the handler after the a task. We will start with the former.
The handlers
block should start after a one-level indentation, that
is, two spaces. It should align with the tasks
block.
Define a handler.
handlers:
- name: restart iis service
win_service:
name: W3Svc
state: restarted
start_mode: auto
Note
You can’t have a former if you don’t mention the latter
handler:
This is telling the play that thetasks:
are over, and now we are defininghandlers:
. Everything below that looks the same as any other task, i.e. you give it a name, a module, and the options for that module. This is the definition of a handler.
notify: restart iis service
…and here is your latter. Finally! Thenotify
statement is the invocation of a handler by name. Quite the reveal, we know. You already noticed that you’ve added anotify
statement to thewin_iis_website
task, now you know why.
Your new, improved playbook is done! But remember we still need to commit the changes to source code control.
Click File
→ Save All
to save the files you’ve written
Click the Source Code icon (1), type in a commit message such as Adding advanced playbook (2), and click the check box above (3).
Sync to gitlab by clicking the arrows on the lower left blue bar. When
prompted, click OK
to push and pull commits.
It should take 5-30 seconds to finish the commit. The blue bar should stop rotating and indicate 0 problems…
Now let’s take a second look to make sure everything looks the way you intended. If not, now is the time for us to fix it up. The playbook below should execute successfully.
---
- hosts: windows
name: This is a play within a playbook
vars:
iis_sites:
- name: 'Ansible Playbook Test'
port: '8080'
path: 'C:\sites\playbooktest'
- name: 'Ansible Playbook Test 2'
port: '8081'
path: 'C:\sites\playbooktest2'
iis_test_message: "Hello World! My test IIS Server"
tasks:
- name: Install IIS
win_feature:
name: Web-Server
state: present
- name: Create site directory structure
win_file:
path: "{{ item.path }}"
state: directory
with_items: "{{ iis_sites }}"
- name: Create IIS site
win_iis_website:
name: "{{ item.name }}"
state: started
port: "{{ item.port }}"
physical_path: "{{ item.path }}"
with_items: "{{ iis_sites }}"
notify: restart iis service
- name: Open port for site on the firewall
win_firewall_rule:
name: "iisport{{ item.port }}"
enable: yes
state: present
localport: "{{ item.port }}"
action: Allow
direction: In
protocol: Tcp
with_items: "{{ iis_sites }}"
- name: Template simple web site to iis_site_path as index.html
win_template:
src: 'index.html.j2'
dest: '{{ item.path }}\index.html'
with_items: "{{ iis_sites }}"
- name: Show website addresses
debug:
msg: "{{ item }}"
loop:
- http://{{ ansible_host }}:8080
- http://{{ ansible_host }}:8081
handlers:
- name: restart iis service
win_service:
name: W3Svc
state: restarted
start_mode: auto
Before we can create our Job Template, you must first go resync your Project again. So do that now.
Note
You must do this anytime you create a new base playbook file that you will be selecting via a Job Template. The new file must be synced to Controller before it will become available in the Job Template playbook dropdown.
To test this playbook, we need to create a new Job Template to run this
playbook. So go to Template and click Add and select Job Template
to create a second job template.
Complete the form using the following values
Key | Value | Note |
---|---|---|
Name | IIS Advanced | |
Description | Template for iis_advanced | |
Job Type | Run | |
Inventory | Workshop Inventory | |
Execution Environment | windows workshop execution environment | |
Project | Ansible Workshop Project | |
Playbook | iis_advanced/site.yml |
|
Credentials | Workshop Credential | |
OPTIONS | [*] Enable Fact Storage |
Click SAVE and on the following page, select the Survey tab.
Create a new survey with following values
Key | Value | Note |
---|---|---|
Question | Please enter a test message for your new website | |
Description | Website test message prompt | |
Answer Variable Name | iis_test_message |
|
Answer Type | Text | |
Minimum/Maximum Length | Keep the defaults | |
Default Answer | Be creative, keep it clean, we’re all professionals here |
Select SAVE and remember to flip the On switch
Now let’s run it and see how it works.
Select TEMPLATES
Note
Alternatively, if you haven’t navigated away from the job templates creation page, you can scroll down to see all existing job templates
Click the rocketship icon for the IIS Advanced Job Template.
When prompted, enter your desired test message
After it launches, you should be redirected and can watch the output of the job in real time.
When the job has successfully completed, you should see two URLs to your websites printed at the bottom of the job output.