Skip to content

Ansible Conventions

Conventions for writing, organizing, and running Ansible playbooks in the CWIQ infrastructure. Consistent structure prevents drift and makes multi-environment deployments safe.


Multi-Environment Inventory Structure

All service playbooks use the inventory directory pattern recommended by the Ansible documentation. Each environment gets its own directory with completely separate hosts and variables. This means all environments coexist on disk — there is no file swapping.

{service-playbook}/
├── inventory/
│   ├── shared/
│   │   ├── hosts.yml              ← hosts + connection settings for shared env
│   │   └── group_vars/
│   │       └── all.yml            ← shared env variables (NOT in git)
│   ├── dev/
│   │   ├── hosts.yml              ← hosts + connection settings for dev env
│   │   └── group_vars/
│   │       └── all.yml            ← dev env variables (NOT in git)
│   └── demo/
│       ├── hosts.yml
│       └── group_vars/
│           └── all.yml
├── group_vars/
│   ├── all.yml.template           ← shared defaults template (IN git)
│   ├── all-dev.yml.template       ← dev template (IN git)
│   └── all-demo.yml.template      ← demo template (IN git)
├── roles/
├── setup.yml
└── deploy-service.yml

Run with: ansible-playbook -i inventory/dev/ setup.yml

This is safer than single-file group_vars because environment configs never overlap.


Template File Convention

CRITICAL: Never create actual group_vars files in the repo

The inventory/{env}/group_vars/all.yml files on the ansible server contain real credentials. Creating them in the repo would overwrite production configuration on git pull.

File type Location Status in git
Template (defaults, no secrets) group_vars/all.yml.template Tracked
Template (env-specific defaults) group_vars/all-{env}.yml.template Tracked
Actual group_vars (real values) inventory/{env}/group_vars/all.yml Gitignored
Actual inventory inventory/{env}/hosts.yml Gitignored

On the ansible server, operators create actual files from templates once:

mkdir -p inventory/dev/group_vars
cp group_vars/all-dev.yml.template inventory/dev/group_vars/all.yml
# Edit inventory/dev/group_vars/all.yml with real credentials

Ansible Server Rules

CRITICAL: Ansible Server is Always on main

The ansible server at /data/ansible/cwiq-ansible-playbooks/ MUST always be on the main branch. Never checkout a feature branch on the ansible server.

Correct Workflow

Work on feature branches locally, merge to main, then pull on the ansible server:

# 1. Make changes on feature branch locally
git checkout -b feature/ansible-new-playbook main
# ... edit files ...
git push origin feature/ansible-new-playbook

# 2. Create MR in GitLab, merge to main

# 3. Only after merge — pull on ansible server
ssh ansible@ansible-shared-cwiq-io
ansible-helper   # cd + venv + vault auth
git pull origin main

# 4. Run playbook from main
ansible-playbook -i inventory/dev/ deploy-service.yml

Never: - git checkout feature/* on the ansible server - git pull origin feature/* on the ansible server - Run playbooks from a feature branch on the ansible server

ansible-helper Function

ansible-helper is a bash alias in the ansible user's shell that automates the three setup steps:

# What ansible-helper does (equivalent):
cd /data/ansible/cwiq-ansible-playbooks
source .venv/bin/activate
# Logs into Vault with AppRole and exports ROLE_ID + SECRET_ID

Always use ansible-helper before running any playbook. Running ansible-playbook without it means Vault credentials are not set, causing playbooks that read secrets to fail.


Role Structure

Each role follows the standard Ansible directory layout:

roles/{role_name}/
├── defaults/
│   └── main.yml        ← default variable values (overridable)
├── tasks/
│   └── main.yml        ← task definitions
├── handlers/
│   └── main.yml        ← handlers (e.g., restart service)
├── templates/
│   └── *.j2            ← Jinja2 templates
├── files/
│   └── *               ← static files to copy
├── vars/
│   └── main.yml        ← role-internal variables (not overridable)
└── meta/
    └── main.yml        ← role dependencies

Key conventions: - defaults/main.yml contains variables that callers may override - vars/main.yml contains internal constants that should not be overridden - Templates use .j2 extension - Tasks are broken into multiple files and included from main.yml for long roles


Variable Precedence

Variables applied in this order (last wins):

  1. defaults/main.yml — role defaults
  2. group_vars/all.yml.template — shared defaults template
  3. inventory/{env}/group_vars/all.yml — environment-specific values (real secrets here)
  4. Per-host variables in inventory/{env}/hosts.yml
  5. Extra vars on command line (-e "key=value")

Playbook Naming Conventions

Type Pattern Example
Full deployment setup.yml or deploy-{service}.yml deploy-prometheus.yml
Config-only deployment deploy-config.yml deploy-config.yml --tags dev
Health check healthcheck.yml healthcheck.yml
Restart restart.yml restart.yml
Stop stop.yml stop.yml
SSL-specific ssl-deploy-{service}.yml ssl-deploy-vault.yml
SSO setup provision-sso-users.yml provision-sso-users.yml