In the article How to use Ansible to configure Vim, I developed an Ansible playbook to configure an initial Vim environment using a few Vim plugins. In this current article, I continue building on the previous example by converting the playbook into an Ansible role.
Ansible roles allow you to develop reusable automation components by grouping and encapsulating related automation artifacts, like configuration files, templates, tasks, and handlers. Because roles isolate these components, it's easier to reuse them and share them with other people. You can also make your roles configurable by exposing variables that users can set when calling the role, allowing them to configure their system according to specific requirements.
[ You might also like: The four things you must be able to do in Vim ]
In this article, I convert the original playbook vim-config.yaml
into a reusable role. At this time, I won't add any new functionality, but I'll further expand this example in the next article. You can find the original playbook and vimrc
configuration file here.
1. Starting a new role
To create an Ansible role, it's enough to make a directory following the standard directory structure documented in the official documentation.
To make it easier and follow the standard, use the ansible-galaxy role init role_name
command to create this directory for you. This command creates the required structure, including a few templates for documentation that you can update. Use it to initialize the vim
role under the roles
directory. First, create the roles
directory and switch to it:
$ mkdir roles
$ cd roles
Then, use the command ansible-galaxy
to initialize the role:
$ ansible-galaxy role init vim
- Role vim was created successfully
Now, verify the role directory structure:
$ tree vim
vim
├── defaults
│ └── main.yml
├── files
├── handlers
│ └── main.yml
├── meta
│ └── main.yml
├── README.md
├── tasks
│ └── main.yml
├── templates
├── tests
│ ├── inventory
│ └── test.yml
└── vars
└── main.yml
8 directories, 8 files
While not required for the role to work, it's highly recommended to document your role by updating the files README.md
and meta/main.yml
. If your role depends on other roles to execute, it's important to document these dependencies in meta/main.yml
, allowing Ansible to download them automatically if required.
Switch into the newly created directory:
$ cd vim
Your Vim role does not require any dependencies. Here's an example of a working meta configuration file. Update it with your name, company name, and a suitable license, if necessary:
$ vim meta/main.yml
galaxy_info:
author: <YOUR NAME>
description: Deploy and configure Vim with plugins
company: <YOUR COMPANY>
license: MIT
min_ansible_version: 2.8
platforms:
- name: Fedora
versions:
- 33
galaxy_tags: []
dependencies: []
The original file has additional comments, which I removed for brevity.
Next, define the tasks to execute.
2. Defining tasks
Generally speaking, your role will execute one or more tasks to configure the target system according to the role's requirements. In this case, you'll want to install and configure Vim. By default, when you execute a role, it looks for a file named main.yml
in the tasks
subdirectory and execute all the tasks listed within it. You can break the tasks into multiple files for more complex roles and call them from main.yml
using the include_tasks
or import_tasks
modules.
For this role, include all required tasks in the tasks/main.yml
file:
$ vim tasks/main.yml
---
# tasks file for vim
- name: Install required packages
package:
name: "{{ install_packages }}"
state: present
become: yes
tags:
- install_packages
- name: Ensure .vim/{autoload,bundle} directory exists
file:
path: "{{ item }}"
state: directory
recurse: no
mode: 0750
loop:
- "{{ vim_dir }}"
- "{{ vim_dir }}/autoload"
- "{{ vim_dir }}/bundle"
- name: Ensure Pathogen is in place
get_url:
dest: "{{ vim_dir }}/autoload/pathogen.vim"
url: https://tpo.pe/pathogen.vim
- name: Deploy plugins
git:
dest: "{{ vim_dir }}/bundle/{{ item.name }}"
repo: "{{ item.url }}"
clone: yes
update: yes
recursive: no
loop: "{{ plugins }}"
- name: Ensure .vimrc config in place
copy:
src: vimrc
dest: "{{ vimrc }}"
backup: yes
mode: 0640
Notice that, unlike the original playbook, you don't include the list of packages or plugins to install directly with the task definition. Instead, you're using the variables install_packages
and plugins
.
By defining variables instead of hard coding the values, you make your roles more reusable and easier to maintain. Now, define values for these variables in two different ways. Start with the plugins
variable, covered next.
3. Defining default variables
When you're developing an Ansible role, you might want to allow role users to provide values to customize how the role performs its tasks. These variables make your role more reusable, allowing users to modify the outcome based on their specific requirements.
For this example, the plugins
variable allows the users to specify which plugins they want to install with Vim, making the role flexible for their needs. It's recommended to define a default value for it in the defaults/main.yml
file to ensure that the roles execute successfully even if the user does not provide a value to this variable.
This file defines variables with a very low precedence which means Ansible will only use them in case the value wasn't defined anywhere else.
Now, define the default value for the plugins
variable like this:
$ vim defaults/main.yml
---
# defaults file for vim
plugins:
- name: vim-airline
url: https://github.com/vim-airline/vim-airline
- name: nerdtree
url: https://github.com/preservim/nerdtree
- name: fzf-vim
url: https://github.com/junegunn/fzf.vim
- name: vim-gitgutter
url: https://github.com/airblade/vim-gitgutter
- name: vim-fugitive
url: https://github.com/tpope/vim-fugitive
- name: vim-floaterm
url: https://github.com/voldikss/vim-floaterm
In this case, you're defining the default value using the same values from the original playbook, which means that if you call the role without providing a value for this variable, it will behave exactly like the original playbook, installing these six plugins.
Define the internal variables.
4. Defining role variables
Another class of variables is role variables or internal variables. By defining these variables in a separate file from the tasks, you make your role easier to maintain. You can reuse these variables in many places, and it's easier to update them in a central place. However, you don't want to make it too easy for users to override them by setting them in general locations such as the playbook or the inventory.
The variables install_packages
, which defines a list of required packages to install, and vimrc
, which specifies the location of Vim's configuration file, are good examples of internal variables. Define them in vars/main.yml
. This file defines variables with higher precedence that are not easily overridden. Users can still provide values if necessary by explicitly setting them when calling the role, but in this case, you can assume they know what they're doing.
$ vim vars/main.yml
---
# vars file for vim
vim_dir: "{{ ansible_env.HOME }}/.vim"
vimrc: "{{ ansible_env.HOME }}/.vimrc"
install_packages:
- vim-enhanced
- git
- powerline-fonts
- fzf
For more details on how Ansible variables precedence works, consult Understanding variable precedence in the documentation.
5. Copying files
The last step to create this role is to copy the file vimrc
to the files
directory. By default, when using the copy
module as a role task, it will look for files to copy in the files
subdirectory. Define the vimrc
file like this:
$ vim files/vimrc
execute pathogen#infect()
syntax on
filetype plugin indent on
colo darkblue
" Configuration vim Airline
set laststatus=2
let g:airline#extensions#tabline#enabled=1
let g:airline_powerline_fonts=1
" Configuration NERDTree
map <F5> :NERDTreeToggle<CR>
" Configuration floaterm
let g:floaterm_keymap_toggle = '<F12>'
let g:floaterm_width = 0.9
let g:floaterm_height = 0.9
" Configuration Vim.FZF
let g:fzf_preview_window = 'right:50%'
let g:fzf_layout = { 'window': { 'width': 0.9, 'height': 0.6 } }
Save and close the file to complete your role. Now, it's time to define the playbook to use the role.
6. Calling the role from a playbook
Now that your role is complete, you can call it from your playbooks. By default, Ansible looks for roles in the roles
subdirectory relative to the playbook file or the system directory /etc/ansible/roles
. You can also use the Ansible configuration roles_path
to define alternative role locations.
For this example, create a playbook in the same directory where you created the roles
directory. Switch to it:
$ cd ../..
$ ls
roles
Create the playbook vim-config.yaml
, similar to the original playbook but this time, instead of defining the tasks, use the module import_role
to import your new vim
role into the playbook:
$ vim vim-config.yaml
- name: Config Vim with plugins
hosts: localhost
gather_facts: yes
become: no
tasks:
- name: Configure Vim using role
import_role:
name: vim
You can also include the role in the playbook using the module include_role
. I'll discuss the differences between these two modules in a separate article. If you can't wait, check the documentation.
Finally, execute the playbook.
8. Execute the playbook
Execute the playbook using the ansible-playbook
command with the -K
parameter and type your sudo
password to allow Ansible to install system packages.
Note: Backup any existing .vimrc
configuration file before running this playbook.
$ ansible-playbook -K vim-config.yaml
BECOME password:
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'
PLAY [Config Vim with plugins] ***********************************************
TASK [Gathering Facts] *******************************************************
ok: [localhost]
TASK [vim : Install required packages] ***************************************
changed: [localhost]
TASK [Ensure .vim/{autoload,bundle} directory exists] ************************
changed: [localhost] => (item=/home/ricardo/.vim)
changed: [localhost] => (item=/home/ricardo/.vim/autoload)
changed: [localhost] => (item=/home/ricardo/.vim/bundle)
TASK [vim : Ensure Pathogen is in place] *************************************
changed: [localhost]
TASK [vim : Deploy plugins] **************************************************
changed: [localhost] => (item={'name': 'vim-airline', 'url': 'https://github.com/vim-airline/vim-airline'})
changed: [localhost] => (item={'name': 'nerdtree', 'url': 'https://github.com/preservim/nerdtree'})
changed: [localhost] => (item={'name': 'fzf-vim', 'url': 'https://github.com/junegunn/fzf.vim'})
changed: [localhost] => (item={'name': 'vim-gitgutter', 'url': 'https://github.com/airblade/vim-gitgutter'})
changed: [localhost] => (item={'name': 'vim-fugitive', 'url': 'https://github.com/tpope/vim-fugitive'})
changed: [localhost] => (item={'name': 'vim-floaterm', 'url': 'https://github.com/voldikss/vim-floaterm'})
TASK [Ensure .vimrc config in place] *****************************************
changed: [localhost]
PLAY RECAP *******************************************************************
localhost : ok=6 changed=5 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
This playbook runs and executes all tasks in the localhost. If you want to configure a remote system, create an inventory file with the desired systems and update the playbook hosts
list.
[ Looking for more on system automation? Get started with The Automated Enterprise, a free book from Red Hat. ]
Wrap up
Now you have a role that installs and configures Vim that you can reuse and share. In the next article in this series, I'll improve this role by adding a template file to make the configuration even more flexible.
You can also use Molecule to test your roles using containers or virtual machines. If you want to know more about that tool, read my article Developing and Testing Ansible Roles with Molecule and Podman - Part 1 in the official Ansible blog.
For more information about Ansible, consult the official documentation.
About the author
Ricardo Gerardi is Technical Community Advocate for Enable Sysadmin and Enable Architect. He was previously a senior consultant at Red Hat Canada, where he specialized in IT automation with Ansible and OpenShift.
He has been a Linux and open source enthusiast and contributor for over 20 years. He is currently interested in hacking stuff using the Go programming language, and he's the author of Powerful Command-Line Applications in Go: Build Fast and Maintainable Tools. Ricardo also writes regularly about Linux, Vim, and command line tools for Opensource.com and Enable Sysadmin community publications.
Ricardo enjoys spending time with his daughters, reading science fiction books, and playing video games.
Browse by channel
Automation
The latest on IT automation for tech, teams, and environments
Artificial intelligence
Updates on the platforms that free customers to run AI workloads anywhere
Open hybrid cloud
Explore how we build a more flexible future with hybrid cloud
Security
The latest on how we reduce risks across environments and technologies
Edge computing
Updates on the platforms that simplify operations at the edge
Infrastructure
The latest on the world’s leading enterprise Linux platform
Applications
Inside our solutions to the toughest application challenges
Original shows
Entertaining stories from the makers and leaders in enterprise tech
Products
- Red Hat Enterprise Linux
- Red Hat OpenShift
- Red Hat Ansible Automation Platform
- Cloud services
- See all products
Tools
- Training and certification
- My account
- Customer support
- Developer resources
- Find a partner
- Red Hat Ecosystem Catalog
- Red Hat value calculator
- Documentation
Try, buy, & sell
Communicate
About Red Hat
We’re the world’s leading provider of enterprise open source solutions—including Linux, cloud, container, and Kubernetes. We deliver hardened solutions that make it easier for enterprises to work across platforms and environments, from the core datacenter to the network edge.
Select a language
Red Hat legal and privacy links
- About Red Hat
- Jobs
- Events
- Locations
- Contact Red Hat
- Red Hat Blog
- Diversity, equity, and inclusion
- Cool Stuff Store
- Red Hat Summit