Multi Environment Ansible Layout – Part 1

One of the more difficult things about using Ansible is deciding on how to lay things out in the file system.

This is the layout I’ve settled on after a few different tries.

This layout solves the following issue:

  • Multiple environment (development, integration, staging, production)

    • Supports environments living in different AWS accounts

    • Per-environment variables

    • Global variables

  • Hybrid inventory (some hosts from AWS, some locally defined)

  • Secrets and variable are managed using smaller scopes

Overview

The layout looks like this:

.
├── ansible.cfg
├── ansible-roles.yml
├── .gitignore
├── inv/
│   ├── group_vars/
│   │   ├── all/
│   │   │   ├── 10-global-secrets.yml
│   │   │   └── 10-global-variables.yml
│   │   ├── integration/
│   │   │   ├── 10-integration-secrets.yml
│   │   │   └── 10-integration-variables.yml
│   │   └── production/
│   │       ├── 10-production-secrets.yml
│   │       └── 10-production-variables.yml
│   ├── host_vars/
│   │   └── backup.example.com/
│   │       └── 10-ansible.yml
│   ├── int/
│   │   ├── ec2.ini
│   │   ├── ec2.py -> ../.shared/ec2.py
│   │   ├── group_vars -> ../group_vars/
│   │   ├── host_vars -> ../host_vars/
│   │   ├── localhost -> ../.shared/localhost
│   │   └── mapping.yml
│   ├── prod/
│   │   ├── ec2.ini
│   │   ├── ec2.py -> ../.shared/ec2.py
│   │   ├── group_vars -> ../group_vars/
│   │   ├── host_vars -> ../host_vars/
│   │   ├── localhost -> ../.shared/localhost
│   │   └── mapping.yml
│   └── .shared/
│       ├── ec2.py
│       └── localhost
├── playbooks/
│   ├── app.yml
│   ├── db.yml
│   ├── Site.yml
│   └── web.yml
├── roles/
│   └── .gitkeep
├── roles-inline/
└── .vault/
    └── password

A git repository with the full layout can be found here

The term environment is reserved in Ansible. I’ve created two new terms to get around this limitation:

  • environ — a "short" term for an environment (aka int or stage).

  • environment_long — the longer version of an environment (aka integration or production).

These are set using the YAML inventory mapping file inv/<environ>/mapping.yml.

Top-Level Files and Folders

  • ansible.cfg — The Ansible configuration file. We make a few changes from the stock file which ships with Ansible.

    • roles_path — We add the two folders we’re using for roles into the roles path setting

      roles_path    = roles/:roles-inline/
    • vault_password_file — I set this to avoid having to type the password when using the ansible-vault command

      vault_password_file = .vault/password
    • retry_files_enabled — I find the creation of retry files annoying, so I turn it off

      retry_files_enabled = False
    • nocows — Some people find the use of the cowsay command in the Ansible output annoying. If you agree, set the following:

      nocows = 1
  • ansible-roles.yml — The third-party roles file. This file is used with the command ansible-galaxy to install third-party Ansible roles.

    ansible-galaxy install -r ansible-roles.yml -p roles/
  • .gitignore — Git ignore file. This file helps us keep secrets and third-party Ansible roles out of our git history. Currently the contents look like this:

    .vault/
    roles/
  • inv/ Top-level inventory folder. (more on this later)

  • playbooks/ — Playbook storage.

  • roles/ — Storage location for third-party Ansible roles.

    • Notice the .gitkeep file there, we use git add -f roles/.gitkeep to override the gitignore file above to keep this empty directory on checkout.

  • roles-inline — Where we write Ansible roles which we want to be a part of this git repository.

  • .vault — Where we store the password file for the ansible-vault command.

    • Normally this password file would be ignored in git, I’ve added it so that the ansible-vault commands will work for people using my example layout.

Inventory Drill-Down

Tomorrow we’ll cover where the real power for this layout comes from, the inventory!