Configuration Management with Ansible - Pilot
Source: Dev.to
Introduction
Welcome to the first installment of our Ansible series! Whether you’re refreshing your skills or diving into Ansible for the first time, this post will give you a solid foundation. Ansible is a powerful, agentless automation tool for configuration management, application deployment, and orchestration. It’s simple yet scalable, making it ideal for managing everything from a homelab to enterprise infrastructures.
In this opener, we’ll focus on how to structure your Ansible repositories effectively—debating the merits of a single repo versus separating variables and secrets. We’ll also walk through a real‑world scenario for handling diverse hosts, emphasize security (because automation without safeguards is a recipe for disaster), and visualize a typical workflow with a Mermaid diagram. By the end, you’ll have a blueprint to start your own projects securely and efficiently.
Future posts will cover advanced topics like role development, testing with Molecule, integrating with cloud providers, and optimizing for large‑scale environments. Let’s jump in!
Choosing Your Repository Structure: Single Repo vs. Separate Vars/Secrets
When starting with Ansible, one of the first decisions is how to organize your code. You have two main approaches:
- Keep everything (playbooks, roles, inventories, variables, and even encrypted secrets) in one repository.
- Split variables and secrets into a separate repository.
Both are valid; your choice hinges on team size, security needs, and workflow complexity.
The Single Repository Approach
This is the go‑to for individuals, small teams, or when you’re just getting (re)started. Everything lives in one Git repo, making it easy to clone, develop, and run playbooks locally. A recommended structure looks like this:
ansible-project/
├── inventories/
│ ├── production/ # Host lists for prod env
│ └── staging/ # Separate for non‑prod
├── group_vars/
│ ├── all/
│ │ ├── vars.yml # Non‑sensitive defaults
│ │ └── secrets.yml # Vault‑encrypted sensitive data
│ └── webservers/ # Group‑specific vars
├── host_vars/ # Per‑host overrides
├── playbooks/ # Your main YAML playbooks
├── roles/ # Reusable roles (e.g., common, nginx)
├── ansible.cfg # Config overrides
└── requirements.yml # Galaxy collections/roles
Pros
- Simplicity: Clone once, and you’re ready to run
ansible-playbook site.yml. - Focus on learning: Spend time on Ansible concepts (variable precedence, facts, handlers) instead of managing multiple repos.
- Built‑in security: Use Ansible Vault to encrypt sensitive files or individual variables (
!vault | inline). Vault uses AES‑256 encryption, which is robust for most use cases.
Cons
- Potential for over‑exposure: If your repo is shared, everyone with access can see the structure of secrets (even if encrypted). In larger teams, this might violate least‑privilege principles.
The Separate Vars/Secrets Repository Approach
As your setup grows—think multiple teams, strict compliance (e.g., PCI‑DSS, HIPAA), or environments with siloed access—move sensitive variables and inventories to a dedicated repo. Non‑sensitive playbooks and roles stay in the main repo.
ansible-playbooks/ (public‑ish: playbooks, roles)
ansible-secrets/ (restricted: vars, inventories, Vault files)
Pros
- Enhanced security: Limit access to the secrets repo. Developers can contribute to playbooks without ever touching production credentials.
- Better for environments: One secrets repo per environment (or branches with protections) reduces mix‑ups.
- Compliance‑friendly: Auditors love separation of code (logic) from data (configs/secrets).
Cons
- Added complexity: You’ll need to clone both repos, use
--extra-vars @../secrets/vars.yml, or integrate via Git submodules/symlinks. This can slow onboarding and local testing.
My Recommendation
If you’re refreshing skills or working solo/small team, start with a single repo. It’s simpler and lets you iterate quickly. Encrypt only what’s truly sensitive—don’t Vault everything, as it complicates diffs and reviews. Use multiple Vault passwords or IDs for different environments, e.g.:
ansible-vault encrypt --vault-id prod@prompt prod_secrets.yml
When pain points emerge (e.g., too many people accessing prod secrets), migrate secrets to a separate repo or—better yet—dynamic sources like HashiCorp Vault, AWS Secrets Manager, or Azure Key Vault. Pull secrets at runtime with lookup plugins to avoid Git entirely. This hybrid is a 2026 best practice for zero‑trust environments.
Remember: Security isn’t just about repository structure—it’s about the Vault password. Never commit it! Store it in a password manager, inject via CI/CD secrets, or prompt interactively. Regularly rotate passwords and audit access.
Scenario: Managing Diverse Hosts with a Single Config Run
Let’s make this concrete. Imagine managing a fleet of 100+ servers:
- Web servers in AWS
- Databases in Azure
- Edge devices on‑prem
Configurations vary—prod webs need strict firewalls, staging allows debugging ports, and databases have unique credentials.
With Ansible, handle this in one regular playbook run by leveraging inventories and variables.
Inventory Setup
Use dynamic or static inventories grouped logically.
[webservers]
web1 ansible_host=10.0.1.10
web2 ansible_host=10.0.1.11
[databases]
db1 ansible_host=10.1.2.20
db2 ansible_host=10.1.2.21
[edge_devices]
edge1 ansible_host=192.168.0.5
edge2 ansible_host=192.168.0.6
Playbook Example
---
- name: Configure all hosts
hosts: all
become: true
vars_files:
- "{{ playbook_dir }}/../group_vars/all/vars.yml"
- "{{ playbook_dir }}/../group_vars/all/secrets.yml" # encrypted
tasks:
- name: Ensure common packages are installed
apt:
name: "{{ common_packages }}"
state: present
when: ansible_os_family == 'Debian'
- name: Deploy web server configuration
include_role:
name: nginx
when: "'webservers' in group_names"
- name: Deploy database configuration
include_role:
name: postgresql
when: "'databases' in group_names"
- name: Configure edge device firewall
include_role:
name: edge_firewall
when: "'edge_devices' in group_names"
Workflow Diagram (Mermaid)
flowchart TD
A[Clone Repo] --> B[Load Inventory]
B --> C[Decrypt Vault Files]
C --> D[Run Playbook]
D --> E{Host Group?}
E -->|webservers| F[Apply nginx Role]
E -->|databases| G[Apply postgresql Role]
E -->|edge_devices| H[Apply edge_firewall Role]
F & G & H --> I[Report Results]
Additional inventory snippet:
0.0.1 ansible_host=10.0.0.1 env=prod
web2 ansible_host=10.0.0.2 env=staging
[databases]
db1 ansible_host=192.168.1.1 env=prod db_type=postgres
TL;DR
- Start simple: One repo with Vault‑encrypted secrets.
- Scale securely: Split secrets into a dedicated repo or external secret manager when needed.
- Never store Vault passwords in Git. Rotate them regularly.
- Leverage inventories & group variables to manage heterogeneous environments from a single playbook run.
Variable Hierarchy
Ansible’s precedence loads vars intelligently.
group_vars/all/vars.yml– Global defaults (e.g.,ntp_server: "pool.ntp.org").group_vars/webservers/vars.yml– Group‑specific (e.g.,http_port: 80).host_vars/web1.yml– Host overrides (e.g.,firewall_rules: strict).- Encrypted –
group_vars/all/secrets.ymlfor API keys, DB passwords.
Playbook Execution
A single site.yml applies configs conditionally.
---
- name: Configure all hosts
hosts: all
become: true
roles:
- common # Installs base packages, sets NTP
tasks:
- name: Set up web server
when: "'webservers' in group_names"
include_role:
name: nginx
vars:
nginx_port: "{{ http_port }}"
- name: Secure database
when: "'databases' in group_names"
include_role:
name: postgres
vars:
db_pass: "{{ vault_db_password }}" # From encrypted vars
Run it regularly via cron or CI/CD:
ansible-playbook -i inventories/production site.yml --vault-id prod@password_file
This ensures consistency while respecting differences—prod gets locked down, staging stays flexible.
Security Stress Point
In this multi‑host setup, Vault is crucial. Exposing plaintext passwords could compromise your entire fleet.
- Always use
--diffto review changes. - Limit
becomeprivileges. - Enable Ansible’s
no_logfor sensitive tasks to avoid logging secrets.
Workflow Visualization: From Development to CI/CD Execution
Below is a Mermaid diagram that shows a secure Ansible workflow. Playbooks are developed in Git, pushed, and executed via CI/CD tools (Jenkins, GitLab CI, GitHub Actions, Rundeck, etc.). Security gates (approvals, secret injection) are highlighted.
The flow emphasizes security: secrets never hit Git plaintext, CI/CD handles injection, and approvals prevent unauthorized deploys. In GitHub Actions, for example, use secrets.VAULT_PASSWORD in your workflow YAML.
Wrapping Up: Secure Automation Starts Here
Structuring your Ansible repo thoughtfully—starting simple and scaling securely—sets you up for success.
- Prioritize Vault for secrets.
- Leverage inventories/vars for flexibility.
- Integrate CI/CD for reliable execution.
This approach minimizes risk while maximizing efficiency, especially across diverse environments.
In the next post, we’ll dive into writing your first roles and testing them with Molecule.
Feedback & Support
- Questions or tips? Drop them in the comments or on our feedback portal.
- Found this post helpful? You can support me:




