AnsiblePilot — Master Ansible Automation

AnsiblePilot is the leading resource for learning Ansible automation, DevOps, and infrastructure as code. Browse over 1,400 tutorials covering Ansible modules, playbooks, roles, collections, and real-world examples. Whether you are a beginner or an experienced engineer, our step-by-step guides help you automate Linux, Windows, cloud, containers, and network infrastructure.

Popular Topics

About Luca Berton

Luca Berton is an Ansible automation expert, author of 8 Ansible books published by Apress and Leanpub including "Ansible for VMware by Examples" and "Ansible for Kubernetes by Example", and creator of the Ansible Pilot YouTube channel. He shares practical automation knowledge through tutorials, books, and video courses to help IT professionals and DevOps engineers master infrastructure automation.

AAP 2.6 CI/CD Pipeline Integration: GitOps Workflows with Jenkins, GitLab, and GitHub Actions

By Luca Berton · Published 2024-01-01 · Category: installation

Integrate AAP 2.6 into CI/CD pipelines with Jenkins, GitLab CI, and GitHub Actions. GitOps workflows, webhook triggers, Infrastructure as Code testing.

CI/CD with AAP 2.6

AAP 2.6 fits into CI/CD pipelines in two ways: as a target (pipelines trigger AAP jobs) and as an orchestrator (AAP workflows drive the full deployment). Most enterprises use both — CI builds and tests in the pipeline, then hands off to AAP for infrastructure deployment.

See also: Ansible vs GitHub Actions: Key Differences & When to Use Each (2026)

Integration Patterns

| Pattern | How It Works | Best For | |---------|-------------|----------| | Webhook trigger | Git push → AAP launches job | Simple GitOps | | API trigger | Pipeline calls AAP REST API | Full pipeline control | | Pipeline plugin | Native Jenkins/GitLab plugin | Tight integration | | Hybrid | Pipeline builds → AAP deploys | Separation of concerns |

GitHub Actions

Trigger AAP Job from GitHub Actions

# .github/workflows/deploy.yml
name: Deploy via AAP

on: push: branches: [main] paths: - 'ansible/**' - 'inventory/**'

jobs: deploy: runs-on: ubuntu-latest steps: - name: Trigger AAP Job Template uses: actions/github-script@v7 env: AAP_URL: ${{ secrets.AAP_URL }} AAP_TOKEN: ${{ secrets.AAP_TOKEN }} with: script: | const response = await fetch( `${process.env.AAP_URL}/api/controller/v2/job_templates/42/launch/`, { method: 'POST', headers: { 'Authorization': `Bearer ${process.env.AAP_TOKEN}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ extra_vars: { git_sha: context.sha, git_ref: context.ref, deployer: context.actor, version: `${context.runNumber}` } }) } ); const job = await response.json(); console.log(`AAP Job launched: ${job.id}`); core.setOutput('job_id', job.id);

- name: Wait for AAP Job uses: actions/github-script@v7 env: AAP_URL: ${{ secrets.AAP_URL }} AAP_TOKEN: ${{ secrets.AAP_TOKEN }} with: script: | const jobId = '${{ steps.trigger.outputs.job_id }}'; let status = 'pending'; while (['pending', 'waiting', 'running'].includes(status)) { await new Promise(r => setTimeout(r, 10000)); const res = await fetch( `${process.env.AAP_URL}/api/controller/v2/jobs/${jobId}/`, { headers: { 'Authorization': `Bearer ${process.env.AAP_TOKEN}` } } ); const job = await res.json(); status = job.status; console.log(`Job ${jobId}: ${status}`); } if (status !== 'successful') { core.setFailed(`AAP job ${jobId} finished with status: ${status}`); }

AAP Webhook Receiver

Configure the job template to accept GitHub webhooks directly:

- name: Configure webhook-triggered template
  ansible.platform.job_template:
    controller_host: "{{ gateway_url }}"
    controller_username: "{{ controller_user }}"
    controller_password: "{{ controller_pass }}"
    name: "Deploy on Push"
    project: "Infrastructure"
    playbook: "deploy.yml"
    inventory: "Production"
    webhook_service: github
    webhook_credential: "GitHub Webhook Secret"
    state: present

GitHub webhook URL: https://gateway.example.org/api/controller/v2/job_templates//github/

See also: Automate Ansible Galaxy Roles with GitHub Actions

GitLab CI

GitLab Pipeline

# .gitlab-ci.yml
stages:
  - lint
  - test
  - deploy

lint: stage: lint image: quay.io/ansible/ansible-navigator:latest script: - ansible-navigator lint playbooks/ -m stdout

test: stage: test image: quay.io/ansible/ansible-navigator:latest script: - ansible-navigator run playbooks/test.yml -m stdout --check --diff -i inventory/staging

deploy_staging: stage: deploy script: - | JOB=$(curl -s -k -X POST \ -H "Authorization: Bearer $AAP_TOKEN" \ -H "Content-Type: application/json" \ -d "{\"extra_vars\": {\"env\": \"staging\", \"version\": \"$CI_COMMIT_SHORT_SHA\"}}" \ "$AAP_URL/api/controller/v2/job_templates/42/launch/") JOB_ID=$(echo $JOB | jq -r '.id') echo "Launched AAP job $JOB_ID" # Poll for completion while true; do STATUS=$(curl -s -k \ -H "Authorization: Bearer $AAP_TOKEN" \ "$AAP_URL/api/controller/v2/jobs/$JOB_ID/" | jq -r '.status') echo "Job $JOB_ID: $STATUS" [ "$STATUS" = "successful" ] && break [ "$STATUS" = "failed" ] || [ "$STATUS" = "error" ] || [ "$STATUS" = "canceled" ] && exit 1 sleep 10 done environment: name: staging only: - main

deploy_production: stage: deploy script: - | curl -s -k -X POST \ -H "Authorization: Bearer $AAP_TOKEN" \ -H "Content-Type: application/json" \ -d "{\"extra_vars\": {\"env\": \"production\", \"version\": \"$CI_COMMIT_SHORT_SHA\"}}" \ "$AAP_URL/api/controller/v2/workflow_job_templates/10/launch/" environment: name: production when: manual only: - main

Jenkins

Jenkins Pipeline

// Jenkinsfile
pipeline {
    agent any

environment { AAP_URL = credentials('aap-url') AAP_TOKEN = credentials('aap-token') }

stages { stage('Lint') { steps { sh 'ansible-navigator lint playbooks/ -m stdout --ee false' } }

stage('Deploy to Staging') { steps { script { def response = httpRequest( url: "${AAP_URL}/api/controller/v2/job_templates/42/launch/", httpMode: 'POST', customHeaders: [[name: 'Authorization', value: "Bearer ${AAP_TOKEN}"]], contentType: 'APPLICATION_JSON', requestBody: """{"extra_vars": { "env": "staging", "version": "${env.BUILD_NUMBER}", "git_sha": "${env.GIT_COMMIT}" }}""" ) def job = readJSON text: response.content env.AAP_JOB_ID = job.id echo "AAP Job launched: ${job.id}" } } }

stage('Wait for Staging') { steps { script { waitForAAPJob(env.AAP_JOB_ID) } } }

stage('Approve Production') { steps { input message: 'Deploy to production?', ok: 'Deploy' } }

stage('Deploy to Production') { steps { script { def response = httpRequest( url: "${AAP_URL}/api/controller/v2/workflow_job_templates/10/launch/", httpMode: 'POST', customHeaders: [[name: 'Authorization', value: "Bearer ${AAP_TOKEN}"]], contentType: 'APPLICATION_JSON', requestBody: """{"extra_vars": {"env": "production", "version": "${env.BUILD_NUMBER}"}}""" ) def job = readJSON text: response.content waitForAAPJob(job.id) } } } } }

def waitForAAPJob(jobId) { timeout(time: 30, unit: 'MINUTES') { while (true) { def resp = httpRequest( url: "${AAP_URL}/api/controller/v2/jobs/${jobId}/", customHeaders: [[name: 'Authorization', value: "Bearer ${AAP_TOKEN}"]] ) def job = readJSON text: resp.content echo "Job ${jobId}: ${job.status}" if (job.status == 'successful') return if (job.status in ['failed', 'error', 'canceled']) { error "AAP job ${jobId} ${job.status}" } sleep 10 } } }

See also: Automate Ansible Collection Testing with GitHub Actions

GitOps Workflow Pattern

[Developer pushes to Git]
        ↓
[CI Pipeline: lint + test]
        ↓ pass
[Pipeline triggers AAP webhook]
        ↓
[AAP: Project Sync (git pull)]
        ↓
[AAP: Deploy to Staging]
        ↓ success
[AAP: Run Integration Tests]
        ↓ success
[AAP: Approval Gate (workflow)]
        ↓ approved
[AAP: Deploy to Production (rolling)]
        ↓
[AAP: Post-deploy Verify + Notify]

AAP Workflow for Full GitOps

- name: Create GitOps deployment workflow
  ansible.platform.workflow_job_template:
    controller_host: "{{ gateway_url }}"
    controller_username: "{{ controller_user }}"
    controller_password: "{{ controller_pass }}"
    name: "GitOps Deploy"
    organization: "Operations"
    webhook_service: github
    survey_enabled: true
    survey_spec:
      name: "Deploy Parameters"
      description: ""
      spec:
        - question_name: "Environment"
          variable: target_env
          type: multiplechoice
          choices: ["staging", "production"]
          required: true
        - question_name: "Version/SHA"
          variable: deploy_version
          type: text
          required: true
    state: present

Best Practices

Credential Management

# Store AAP credentials as CI/CD secrets, never in code
# GitHub: Settings → Secrets → AAP_URL, AAP_TOKEN
# GitLab: Settings → CI/CD → Variables
# Jenkins: Credentials → Secret text

# Use short-lived tokens - name: Create CI/CD service credential ansible.platform.token: controller_host: "{{ gateway_url }}" controller_username: "{{ controller_user }}" controller_password: "{{ controller_pass }}" description: "GitLab CI Token" scope: "write" state: present

Idempotent Deployments

# Playbooks should be safe to re-run
- name: Deploy application (idempotent)
  hosts: webservers
  tasks:
    - name: Deploy version
      ansible.builtin.template:
        src: app-config.j2
        dest: /etc/myapp/config.yml
      notify: restart myapp

- name: Ensure desired version running ansible.builtin.uri: url: "http://localhost:8080/health" return_content: true register: health until: health.json.version == deploy_version retries: 30 delay: 10

FAQ

Should the CI pipeline or AAP own the deployment?

Use CI for building, testing, and artifact creation. Use AAP for infrastructure deployment. AAP provides RBAC, credential injection, audit logging, and rollback that CI tools lack.

How do I handle rollbacks?

Configure an AAP workflow with a rollback path. On job failure, the workflow triggers a rollback job template that deploys the previous known-good version. Store version history in AAP survey variables or external CMDB.

Can I use AAP Collections in CI pipelines?

Yes. Install ansible.controller or ansible.platform in your pipeline and use the collection modules to interact with AAP programmatically — launching jobs, creating inventories, managing credentials.

How do I test Ansible playbooks in CI?

Use Molecule for role-level testing, ansible-navigator run --check for syntax/dry-run validation, and ansible-lint for style checks. Run integration tests against ephemeral infrastructure in staging before production.

What about branch-based deployments?

Use AAP project branches — set the SCM branch dynamically via extra_vars or survey. Feature branches deploy to dev environments, main deploys to staging/production.

Conclusion

AAP 2.6 integrates seamlessly into CI/CD pipelines through webhooks, REST API, and native plugins. Whether using GitHub Actions, GitLab CI, or Jenkins, the pattern is consistent: CI handles build and test, AAP handles deployment with enterprise controls. GitOps workflows combine both for fully automated, auditable infrastructure delivery.

Related Articles

AAP 2.6 REST API Guide: Automate the Automation PlatformAAP 2.6 Workflow Templates: Advanced Multi-Step Automation GuideAAP 2.6 Notifications and Webhooks: Slack, Teams, Email, and Custom IntegrationsAnsible GitOps Infrastructure as Code with Git Workflows and AAPAAP 2.6 Credential Management: Vaults, External Secrets, and Machine Credentials

Category: installation

Browse all Ansible tutorials · AnsiblePilot Home