Ansible GitOps: Infrastructure as Code with Git Workflows and AAP
By Luca Berton · Published 2024-01-01 · Category: installation
Implement GitOps workflows with Ansible and AAP. Use Git as single source of truth for infrastructure, automate deployments with webhooks, and enforce change.
Introduction
GitOps is the practice of using Git as the single source of truth for infrastructure and configuration. Every change goes through a pull request, gets reviewed, and triggers automated deployment. When combined with Ansible Automation Platform, GitOps brings enterprise-grade change control to infrastructure automation.
See also: ARA Records Ansible: Playbook Reporting & History (Complete Guide)
GitOps Principles for Ansible
Git is the source of truth — All playbooks, inventories, and variables live in Git Changes via pull requests — No manual changes; everything goes through code review Automated deployment — Merges to main trigger automatic AAP job execution Drift detection — Regular scans detect and remediate manual changes Audit trail — Git history + AAP job logs provide complete change historyRepository Structure
ansible-infrastructure/
├── inventories/
│ ├── production/
│ │ ├── hosts.yml
│ │ └── group_vars/
│ │ ├── all.yml
│ │ └── webservers.yml
│ └── staging/
│ ├── hosts.yml
│ └── group_vars/
├── playbooks/
│ ├── site.yml
│ ├── deploy-app.yml
│ ├── security-baseline.yml
│ └── patch-servers.yml
├── roles/
│ ├── common/
│ ├── webserver/
│ └── database/
├── collections/
│ └── requirements.yml
├── execution-environment/
│ ├── execution-environment.yml
│ └── requirements.txt
├── .github/
│ └── workflows/
│ ├── lint.yml
│ └── deploy.yml
├── ansible.cfg
├── ansible-lint.yml
└── README.md
See also: AAP 2.6 CI/CD Pipeline Integration: GitOps Workflows with Jenkins, GitLab, and GitHub Actions
Git Workflow
Branch Strategy
main (protected)
├── feature/add-monitoring
├── fix/ssh-config
└── hotfix/security-patch
Pull Request Process
1. Developer creates branch
2. Makes changes to playbooks/roles
3. Opens pull request
→ Automated ansible-lint runs
→ Automated syntax check
→ Automated --check mode against staging
4. Peer review and approval
5. Merge to main
→ Webhook triggers AAP job
→ AAP deploys to production
AAP Webhook Integration
GitHub Webhook to AAP
Configure AAP to listen for GitHub webhooks:
AAP: Job Template → Enable Webhook → Copy Webhook URL and Key
GitHub: Repository → Settings → Webhooks → Add webhook
• URL: https://aap.example.com/api/v2/job_templates/42/github/
• Content type: application/json
• Secret: (from AAP webhook key)
• Events: Push events on main branch
Webhook Payload in Playbook
- name: Deploy from webhook trigger
hosts: webservers
vars:
git_ref: "{{ awx_webhook_payload.ref | default('refs/heads/main') }}"
commit_sha: "{{ awx_webhook_payload.after | default('HEAD') }}"
committer: "{{ awx_webhook_payload.pusher.name | default('unknown') }}"
tasks:
- name: Log deployment trigger
ansible.builtin.debug:
msg: "Deploying {{ commit_sha[:8] }} by {{ committer }}"
- name: Pull latest code
ansible.builtin.git:
repo: "{{ app_repo }}"
dest: /opt/myapp
version: "{{ commit_sha }}"
notify: restart app
See also: Ansible Policy and Migration Engine (APME): AI-Powered Playbook Scanning and Compliance
CI/CD Pipeline
GitHub Actions: Lint + Test
# .github/workflows/lint.yml
name: Ansible Lint & Test
on:
pull_request:
paths: ['playbooks/**', 'roles/**', 'inventories/**']
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install ansible-lint
run: pip install ansible-lint yamllint
- name: YAML Lint
run: yamllint .
- name: Ansible Lint
run: ansible-lint playbooks/
- name: Syntax Check
run: ansible-playbook playbooks/site.yml --syntax-check
dry-run:
needs: lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Ansible
run: pip install ansible-core
- name: Dry Run against staging
run: |
ansible-playbook playbooks/site.yml \
-i inventories/staging/hosts.yml \
--check --diff
env:
ANSIBLE_HOST_KEY_CHECKING: "false"
Auto-Trigger AAP on Merge
# .github/workflows/deploy.yml
name: Deploy via AAP
on:
push:
branches: [main]
jobs:
trigger-aap:
runs-on: ubuntu-latest
steps:
- name: Trigger AAP Job Template
run: |
curl -X POST \
"https://aap.example.com/api/v2/job_templates/${{ secrets.AAP_TEMPLATE_ID }}/launch/" \
-H "Authorization: Bearer ${{ secrets.AAP_TOKEN }}" \
-H "Content-Type: application/json" \
-d '{"extra_vars": {"git_sha": "${{ github.sha }}"}}'
Drift Detection
Schedule regular compliance scans to detect manual changes:
# drift-detection.yml
- name: Detect configuration drift
hosts: all
become: true
tasks:
- name: Check SSH config matches desired state
ansible.builtin.lineinfile:
path: /etc/ssh/sshd_config
line: "PermitRootLogin no"
state: present
check_mode: true
register: ssh_drift
- name: Alert on drift
ansible.builtin.debug:
msg: "DRIFT DETECTED: SSH config changed on {{ inventory_hostname }}"
when: ssh_drift.changed
- name: Report drift to monitoring
ansible.builtin.uri:
url: "{{ alertmanager_url }}/api/v1/alerts"
method: POST
body_format: json
body:
- alertname: ConfigDrift
labels:
host: "{{ inventory_hostname }}"
severity: warning
when: ssh_drift.changed
delegate_to: localhost
Encrypted Secrets in Git
# Encrypt sensitive variables with Ansible Vault
ansible-vault encrypt inventories/production/group_vars/all/vault.yml
# Use in CI/CD (vault password from secret)
ansible-playbook site.yml --vault-password-file <(echo "$VAULT_PASSWORD")
AAP Project Configuration
# AAP Project settings
Project:
Name: Infrastructure GitOps
SCM Type: Git
SCM URL: https://github.com/myorg/ansible-infrastructure.git
SCM Branch: main
SCM Update on Launch: true # Always pull latest
SCM Clean: true
Job Template:
Name: Deploy Infrastructure
Project: Infrastructure GitOps
Playbook: playbooks/site.yml
Inventory: Production
Credentials: [SSH Key, Vault Password]
Webhook: Enabled (GitHub)
Best Practices
Protect the main branch — Require PR reviews and passing CI checks Ansible-lint in CI — Catch issues before they reach production Check mode in PRs — Dry-run against staging on every pull request Encrypt secrets — Use Ansible Vault or HashiCorp Vault for sensitive data in Git Tag releases — Semantic versioning for infrastructure changes Automated rollback — If deployment fails, revert to previous Git tag Small, frequent changes — Easier to review and debug than large PRs Drift detection on schedule — Daily scans to catch manual changesFAQ
How to handle emergency changes?
Create a hotfix/ branch, get expedited review (single approver), merge to main. The webhook triggers deployment immediately. Document the emergency in the commit message.
Can I use GitLab instead of GitHub?
Yes — AAP supports GitLab webhooks natively. The workflow is identical; just configure the GitLab project webhook URL from AAP.
How to rollback a bad deployment?
git revert HEAD # Create a revert commit
git push origin main # Webhook triggers deployment of reverted state
Conclusion
GitOps with Ansible and AAP creates an auditable, reviewable, automated infrastructure pipeline. Every change is tracked in Git, reviewed by peers, tested in CI, and deployed automatically — turning infrastructure management into a disciplined engineering practice.
Related Articles
• Ansible Automation Platform 2.6 • Ansible Roles Complete Guide • Ansible Check Mode Dry Run GuideCategory: installation