Ansible Automation Platform: A Comprehensive Guide

Ansible Deep Dive — Roles, Collections, Vault, AAP & OCP


1. Roles

What is a Role?

A Role is a structured, reusable unit of automation — it organizes tasks, variables, files, templates, and handlers into a standardized directory layout.

Role Directory Structure
roles/
└── my-webserver/
├── tasks/
│ └── main.yml # Entry point — all tasks
├── handlers/
│ └── main.yml # Handlers (restart, reload)
├── templates/
│ └── nginx.conf.j2 # Jinja2 templates
├── files/
│ └── index.html # Static files to copy
├── vars/
│ └── main.yml # High-priority variables
├── defaults/
│ └── main.yml # Low-priority defaults (overridable)
├── meta/
│ └── main.yml # Role metadata, dependencies
└── README.md
tasks/main.yml
---
- name: Install nginx
ansible.builtin.yum:
name: nginx
state: present
- name: Deploy config from template
ansible.builtin.template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
notify: Restart nginx
- name: Ensure nginx is started
ansible.builtin.service:
name: nginx
state: started
enabled: true
defaults/main.yml
---
nginx_port: 80
nginx_worker_processes: auto
nginx_max_connections: 1024
templates/nginx.conf.j2 (Jinja2)
worker_processes {{ nginx_worker_processes }};
events {
worker_connections {{ nginx_max_connections }};
}
server {
listen {{ nginx_port }};
server_name {{ ansible_hostname }};
}
meta/main.yml — Role Dependencies
---
dependencies:
- role: common
- role: firewall
vars:
open_ports:
- 80
- 443

Using a Role in a Playbook

- name: Configure web servers
hosts: webservers
become: true
roles:
- common
- my-webserver # Simple include
# OR with parameters:
roles:
- role: my-webserver
vars:
nginx_port: 8080
Role Execution Order in a Play
pre_tasks → role dependencies → roles → tasks → post_tasks → handlers

2. Collections

What is a Collection?

A Collection is a distribution format for Ansible content — it packages roles, modules, plugins, playbooks, and docs together under a namespace.

namespace.collection_name
└── ansible.builtin # Core Ansible modules
└── redhat.openshift # OCP-specific modules
└── community.kubernetes # K8s modules
└── amazon.aws # AWS modules
Collection Directory Structure
my_namespace/my_collection/
├── galaxy.yml # Collection metadata
├── README.md
├── roles/
│ └── my_role/
├── plugins/
│ ├── modules/ # Custom modules
│ ├── inventory/ # Dynamic inventory plugins
│ └── filter/ # Custom Jinja2 filters
├── playbooks/
└── docs/
Installing Collections
# From Ansible Galaxy
ansible-galaxy collection install redhat.openshift
# From requirements file (preferred)
ansible-galaxy collection install -r requirements.yml
# From Automation Hub (AAP)
ansible-galaxy collection install redhat.openshift \
--server https://cloud.redhat.com/api/automation-hub/
# Offline / from tarball
ansible-galaxy collection install my_namespace-my_collection-1.0.0.tar.gz
requirements.yml
---
collections:
- name: redhat.openshift
version: ">=2.0.0"
- name: community.kubernetes
version: "2.0.0"
- name: amazon.aws
version: ">=5.0.0"
roles:
- name: geerlingguy.nginx
version: "3.0.0"
Key Collections for OCP/K8s
CollectionUse
redhat.openshiftOCP-specific — routes, builds, operators
kubernetes.coreCore K8s module (k8s, helm, kubectl)
community.kubernetesCommunity K8s modules
redhat.rhel_system_rolesRHEL OS config
ansible.posixPOSIX system modules
amazon.awsAWS provisioning
Using Collection Modules
- name: Deploy OCP application
hosts: localhost
collections:
- redhat.openshift
- kubernetes.core
tasks:
- name: Create namespace
kubernetes.core.k8s:
state: present
definition:
apiVersion: v1
kind: Namespace
metadata:
name: my-app
- name: Deploy from template
kubernetes.core.k8s:
state: present
src: deployment.yaml
- name: Create OCP Route
redhat.openshift.openshift_route:
namespace: my-app
service: my-service
state: present

3. Ansible Vault

What is Vault?

Ansible Vault encrypts sensitive data — passwords, API keys, certificates — at rest in YAML files using AES-256 encryption.

Encrypting Files
# Encrypt an entire file
ansible-vault encrypt secrets.yml
# Decrypt a file
ansible-vault decrypt secrets.yml
# View without decrypting to disk
ansible-vault view secrets.yml
# Edit encrypted file
ansible-vault edit secrets.yml
# Re-key (change password)
ansible-vault rekey secrets.yml
# Encrypt a single string value
ansible-vault encrypt_string 'mysecretpassword' \
--name 'db_password'
Encrypted String Output
# Output of encrypt_string — paste directly into vars file
db_password: !vault |
$ANSIBLE_VAULT;1.1;AES256
61386634653335343832333938343039303664653764323035
64356635333562303937363135626565353537306261363539
...
Vault in Variable Files
# group_vars/all/vault.yml ← encrypted file
vault_db_password: supersecret123
vault_api_key: abc123xyz
# group_vars/all/vars.yml ← plaintext, references vault vars
db_password: "{{ vault_db_password }}"
api_key: "{{ vault_api_key }}"

Running Playbooks with Vault

# Prompt for password
ansible-playbook site.yml --ask-vault-pass
# Use password file
ansible-playbook site.yml --vault-password-file ~/.vault_pass
# Use environment variable
export ANSIBLE_VAULT_PASSWORD_FILE=~/.vault_pass
ansible-playbook site.yml
# Multiple vault IDs (different passwords for different files)
ansible-playbook site.yml \
--vault-id dev@~/.vault_pass_dev \
--vault-id prod@prompt
Multiple Vault IDs
# Encrypt with a specific vault ID label
ansible-vault encrypt_string 'prodpassword' \
--name 'db_password' \
--vault-id prod@~/.vault_pass_prod
# Result:
db_password: !vault |
$ANSIBLE_VAULT;1.2;AES256;prod ← vault ID label
...
Best Practices
  • Never commit plaintext secrets to git
  • Store vault password file outside the repo
  • Use separate vault IDs per environment (dev/staging/prod)
  • Encrypt the vars file, not the whole playbook
  • Use ansible-vault encrypt_string for individual values in otherwise plaintext files

4. Ansible Automation Platform (AAP)

What is AAP?

AAP (formerly Ansible Tower) is the enterprise version of Ansible — adds a Web UI, REST API, RBAC, job scheduling, audit logging, credential management, and execution environments.

AAP Architecture
┌──────────────────────────────────────────────────┐
│ Ansible Automation Platform │
│ │
│ ┌────────────┐ ┌──────────────────────────┐ │
│ │ Automation │ │ Automation Controller │ │
│ │ Hub │ │ (formerly Tower) │ │
│ │(Collections│ │ - Job Templates │ │
│ │ & roles) │ │ - Workflows │ │
│ └────────────┘ │ - Schedules │ │
│ │ - RBAC │ │
│ ┌────────────┐ │ - Credentials │ │
│ │ Event │ └──────────────────────────┘ │
│ │ Driven │ │
│ │ Ansible │ ┌──────────────────────────┐ │
│ │ (EDA) │ │ Execution Environments │ │
│ └────────────┘ │ (containerized runtime) │ │
│ └──────────────────────────┘ │
└──────────────────────────────────────────────────┘

Key AAP Concepts

ConceptDescription
InventoryStatic or dynamic host groups
CredentialStored secrets (SSH, cloud, vault) — never exposed
ProjectSCM-linked (Git) source for playbooks
Job TemplateReusable job definition — playbook + inventory + credential
WorkflowChain multiple job templates with conditions
Execution Environment (EE)Container image with Ansible + dependencies
OrganizationMulti-tenancy unit — groups users, teams, resources
RBACRole-based access — Admin, Execute, Read per resource
NotificationAlerts on job success/failure (Slack, email, webhook)
SurveyRuntime input form for job templates

Execution Environments (EE)
# execution-environment.yml
version: 1
build_arg_defaults:
EE_BASE_IMAGE: registry.redhat.io/ansible-automation-platform/ee-minimal-rhel8
dependencies:
galaxy: requirements.yml
python: requirements.txt
system: bindep.txt
additional_build_steps:
prepend:
- RUN pip3 install boto3
# Build EE with ansible-builder
pip install ansible-builder
ansible-builder build -t my-ee:1.0 -f execution-environment.yml
# Push to registry
podman push my-ee:1.0 registry.example.com/my-ee:1.0

Event-Driven Ansible (EDA)

EDA triggers automation based on events from external sources.

# rulebook.yml
- name: Respond to alerts
hosts: all
sources:
- ansible.eda.webhook: # Listen on webhook
host: 0.0.0.0
port: 5000
- ansible.eda.kafka: # Or Kafka topic
host: kafka.example.com
topic: alerts
rules:
- name: Restart service on alert
condition: event.payload.alertname == "ServiceDown"
action:
run_job_template:
name: Restart Service
organization: Default
- name: Scale up on high CPU
condition: event.payload.metric > 80
action:
run_playbook:
name: scale-up.yml

5. Ansible for OpenShift — Practical Examples

Install OCP Cluster (IPI)
- name: Install OpenShift cluster
hosts: localhost
collections:
- redhat.openshift_installer
tasks:
- name: Generate install-config
template:
src: install-config.yaml.j2
dest: "{{ install_dir }}/install-config.yaml"
- name: Run openshift-install
command: >
openshift-install create cluster
--dir {{ install_dir }}
--log-level info
Day-2 OCP Operations
- name: OCP Day-2 Configuration
hosts: localhost
collections:
- redhat.openshift
- kubernetes.core
tasks:
- name: Create htpasswd identity provider
kubernetes.core.k8s:
state: present
definition: "{{ lookup('template', 'oauth.yaml.j2') }}"
- name: Apply resource quotas
kubernetes.core.k8s:
state: present
definition:
apiVersion: v1
kind: ResourceQuota
metadata:
name: team-quota
namespace: "{{ item }}"
spec:
hard:
pods: "20"
requests.cpu: "4"
requests.memory: 8Gi
loop: "{{ namespaces }}"
- name: Deploy OADP operator
kubernetes.core.k8s:
state: present
src: oadp-subscription.yaml
- name: Wait for operator to be ready
kubernetes.core.k8s_info:
kind: ClusterServiceVersion
namespace: openshift-adp
register: csv
until: >
csv.resources | selectattr('status.phase','equalto','Succeeded') | list | length > 0
retries: 20
delay: 30

Interview Quick-Fire

  • Difference between vars and defaults in a role? defaults lowest priority — easily overridden. vars higher priority — harder to override.
  • What is ansible_facts? Auto-gathered system info (OS, IP, memory) available as variables.
  • How do you run only specific tasks? Use --tags or --skip-tags.
  • What is delegate_to? Run a task on a different host than the current one.
  • Difference between include_tasks and import_tasks? import is static (parsed at startup); include is dynamic (evaluated at runtime — supports loops/conditions).
  • What is an inventory plugin? Dynamically generates inventory from external sources (AWS, Azure, OCP).
  • How do you test Ansible roles? Using Molecule — spins up containers/VMs, runs the role, verifies with testinfra or ansible assertions.

Leave a Reply