Ansible CI/CD Pipeline Integration
Manual playbook execution — running ansible-playbook from your laptop — works for learning and one-off tasks, but it is not how professional teams operate. In production, playbooks run automatically: triggered by code commits, pull request merges, scheduled maintenance windows, or monitoring alerts. This lesson teaches you to integrate Ansible into two of the most widely used CI/CD platforms: GitHub Actions and Jenkins.
Why Automate Playbook Execution
Automating Ansible execution through a CI/CD pipeline delivers several critical benefits:
- Consistency: Every deployment follows the exact same process regardless of which engineer triggered it
- Auditability: The CI/CD system logs every deployment with timestamps, triggered-by identity, and full output
- Gate enforcement: Linting, syntax checks, and tests run automatically before any playbook touches production
- Reduced human error: No one can accidentally skip the vault password, forget to pull latest changes, or run against the wrong inventory
GitHub Actions: Deploying with Ansible on Push
GitHub Actions workflows are defined in .github/workflows/ YAML files in your repository.
Create .github/workflows/deploy.yml:
name: Deploy LAMP Stack
on:
push:
branches: [main]
workflow_dispatch: # Allow manual triggers from GitHub UI
jobs:
lint:
name: Lint and Syntax Check
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install Ansible and tools
run: |
pip install ansible ansible-lint yamllint
- name: Run yamllint
run: yamllint .
- name: Run ansible-lint
run: ansible-lint
- name: Syntax check all playbooks
run: ansible-playbook site.yml --syntax-check -i inventory/
deploy:
name: Deploy to Production
runs-on: ubuntu-latest
needs: lint # Only deploy if lint passes
environment: production # GitHub environment with required reviewers
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Python and Ansible
run: pip install ansible boto3
- name: Install Galaxy requirements
run: ansible-galaxy install -r requirements.yml
- name: Write Vault password
run: echo "${{ secrets.ANSIBLE_VAULT_PASSWORD }}" > .vault_pass
# File is created only in the runner's ephemeral environment
- name: Write SSH private key
run: |
mkdir -p ~/.ssh
echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/ansible_ed25519
chmod 600 ~/.ssh/ansible_ed25519
- name: Run deployment playbook
run: |
ansible-playbook site.yml \
-i inventory/ \
--vault-password-file .vault_pass \
--private-key ~/.ssh/ansible_ed25519 \
-v
- name: Clean up secrets
if: always() # Run even if deployment fails
run: |
rm -f .vault_pass ~/.ssh/ansible_ed25519Storing Secrets in GitHub Actions
Never put passwords or private keys in your workflow file or repository. Use GitHub's encrypted Secrets:
- Go to your GitHub repository → Settings → Secrets and variables → Actions
- Create
ANSIBLE_VAULT_PASSWORDwith your Vault password value - Create
SSH_PRIVATE_KEYwith the contents of your private key file
These secrets are injected as environment variables during the workflow run and are never exposed in logs.
Jenkins Pipeline: Declarative Jenkinsfile
Create a Jenkinsfile in your repository root:
pipeline {
agent {
docker {
image 'python:3.11-slim'
args '-v /home/jenkins/.ssh:/root/.ssh:ro'
}
}
environment {
ANSIBLE_HOST_KEY_CHECKING = 'False'
ANSIBLE_FORCE_COLOR = 'true'
}
stages {
stage('Setup') {
steps {
sh 'pip install ansible ansible-lint'
sh 'ansible-galaxy install -r requirements.yml'
}
}
stage('Lint') {
steps {
sh 'ansible-lint'
sh 'ansible-playbook site.yml --syntax-check -i inventory/'
}
}
stage('Deploy to Staging') {
steps {
withCredentials([string(credentialsId: 'vault-password', variable: 'VAULT_PASS')]) {
sh '''
echo "$VAULT_PASS" > .vault_pass
ansible-playbook site.yml \
-i inventory/staging/ \
--vault-password-file .vault_pass
rm -f .vault_pass
'''
}
}
}
stage('Smoke Test') {
steps {
sh 'ansible-playbook tests/smoke-test.yml -i inventory/staging/'
}
}
stage('Deploy to Production') {
when {
branch 'main'
}
input {
message "Deploy to production?"
ok "Yes, deploy"
submitter "admin,devlead"
}
steps {
withCredentials([string(credentialsId: 'vault-password-prod', variable: 'VAULT_PASS')]) {
sh '''
echo "$VAULT_PASS" > .vault_pass
ansible-playbook site.yml \
-i inventory/production/ \
--vault-password-file .vault_pass \
-v
rm -f .vault_pass
'''
}
}
}
}
post {
always {
sh 'rm -f .vault_pass'
cleanWs()
}
failure {
emailext(
subject: "Deployment FAILED: ${env.JOB_NAME} #${env.BUILD_NUMBER}",
body: "Check Jenkins: ${env.BUILD_URL}",
to: 'devops@example.com'
)
}
}
}Key CI/CD Pipeline Patterns
Lint before deploy. Always run ansible-lint and --syntax-check as the first stage. Failed linting blocks the deployment, preventing obviously broken playbooks from touching infrastructure.
Staging before production. Deploy to a staging environment first, run automated tests, and only proceed to production after staging passes. The Jenkins example above uses an input step to require human approval before production — appropriate for sensitive deployments.
Ephemeral secrets. Write secrets to temporary files only during the pipeline run and explicitly delete them in a post: always block or cleanup step. This limits the window during which secrets exist on the CI runner's filesystem.
Immutable artifacts. Tag your Ansible code at the same time as your application code. Deploy the same tagged version through staging and production to guarantee consistency.
Try This: Add Ansible to a GitHub Repository
Push your LAMP stack project from Topic 28 to a GitHub repository. Create a GitHub Actions workflow that runs ansible-lint and --syntax-check on every pull request. Add your Vault password as a GitHub Secret. Make a deliberate syntax error in a playbook, open a pull request, and confirm the workflow fails and blocks the merge. Then fix the error and watch the workflow pass. This end-to-end experience with CI/CD-gated Ansible deployments is a marketable skill.
Summary
Integrating Ansible with CI/CD platforms converts manual playbook execution into a consistent, auditable, gated automation workflow. GitHub Actions workflows defined in YAML run on code pushes and use encrypted Secrets for credentials. Jenkins Declarative Pipelines support multi-stage deployments with approval gates. Both platforms follow the same core pattern: lint, syntax-check, deploy to staging, test, deploy to production. Ephemeral secret handling is critical for security in all CI/CD Ansible integrations.
