CI/CD Integration¶
Integrate hier-config-cli into your continuous integration and deployment pipelines to automate configuration validation, testing, and deployment.
Overview¶
Using hier-config-cli in CI/CD pipelines enables:
- Automated validation of configuration changes
- Pull request checks to catch issues early
- Automated remediation generation for approved changes
- Configuration drift detection in production
- Compliance checking against golden configs
GitHub Actions¶
Basic Workflow¶
.github/workflows/config-validation.yml:
name: Network Configuration Validation
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
paths:
- 'configs/**'
jobs:
validate-configs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install hier-config-cli
run: |
pip install hier-config-cli
- name: Validate configurations
run: |
for device in configs/intended/*.conf; do
device_name=$(basename "$device" .conf)
echo "Validating $device_name..."
hier-config-cli remediation \
--platform ios \
--running-config "configs/running/${device_name}.conf" \
--generated-config "$device" \
--output "remediation/${device_name}.txt"
done
- name: Upload remediation artifacts
uses: actions/upload-artifact@v4
with:
name: remediation-configs
path: remediation/
Advanced Workflow with Matrix¶
.github/workflows/multi-platform-validation.yml:
name: Multi-Platform Configuration Validation
on:
pull_request:
branches: [main]
jobs:
validate:
runs-on: ubuntu-latest
strategy:
matrix:
platform: [ios, nxos, iosxr, eos]
fail-fast: false
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
cache: 'pip'
- name: Install dependencies
run: |
pip install hier-config-cli
- name: Generate remediation for ${{ matrix.platform }}
run: |
mkdir -p output/remediation output/rollback
for config in configs/intended/${{ matrix.platform }}/*.conf; do
if [ -f "$config" ]; then
device=$(basename "$config" .conf)
echo "Processing $device (${{ matrix.platform }})"
# Generate remediation
hier-config-cli remediation \
--platform ${{ matrix.platform }} \
--running-config "configs/running/${{ matrix.platform }}/${device}.conf" \
--generated-config "$config" \
--output "output/remediation/${device}.txt"
# Generate rollback
hier-config-cli rollback \
--platform ${{ matrix.platform }} \
--running-config "configs/running/${{ matrix.platform }}/${device}.conf" \
--generated-config "$config" \
--output "output/rollback/${device}.txt"
fi
done
- name: Check for excessive changes
run: |
for file in output/remediation/*.txt; do
if [ -f "$file" ]; then
lines=$(wc -l < "$file")
if [ "$lines" -gt 100 ]; then
echo "::error::Too many changes in $(basename "$file"): $lines lines"
exit 1
fi
fi
done
- name: Upload artifacts
uses: actions/upload-artifact@v4
if: always()
with:
name: configs-${{ matrix.platform }}
path: output/
- name: Comment on PR
uses: actions/github-script@v7
if: github.event_name == 'pull_request'
with:
script: |
const fs = require('fs');
const files = fs.readdirSync('output/remediation');
let comment = '## Configuration Validation Results - ${{ matrix.platform }}\n\n';
comment += `Found ${files.length} devices to update\n\n`;
for (const file of files) {
const content = fs.readFileSync(`output/remediation/${file}`, 'utf8');
const lines = content.split('\n').length;
comment += `- **${file}**: ${lines} configuration lines\n`;
}
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: comment
});
Scheduled Compliance Check¶
.github/workflows/compliance-check.yml:
name: Configuration Compliance Check
on:
schedule:
# Run every day at 2 AM UTC
- cron: '0 2 * * *'
workflow_dispatch:
jobs:
compliance-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install dependencies
run: |
pip install hier-config-cli
- name: Fetch running configs
run: |
# This would be replaced with actual config fetching logic
# For example, using Ansible, NAPALM, or network APIs
echo "Fetching configs from devices..."
- name: Check for drift
id: drift-check
run: |
mkdir -p drift-reports
drift_detected=false
for device in configs/golden/*.conf; do
device_name=$(basename "$device" .conf)
hier-config-cli remediation \
--platform ios \
--running-config "configs/running/${device_name}.conf" \
--generated-config "$device" \
--output "drift-reports/${device_name}.txt" \
--format json
if [ -s "drift-reports/${device_name}.txt" ]; then
drift_detected=true
fi
done
echo "drift_detected=$drift_detected" >> $GITHUB_OUTPUT
- name: Create issue if drift detected
if: steps.drift-check.outputs.drift_detected == 'true'
uses: actions/github-script@v7
with:
script: |
github.rest.issues.create({
owner: context.repo.owner,
repo: context.repo.repo,
title: 'Configuration Drift Detected',
body: 'Automated compliance check detected configuration drift. Please review the attached artifacts.',
labels: ['configuration', 'drift', 'automated']
});
- name: Upload drift reports
if: always()
uses: actions/upload-artifact@v4
with:
name: drift-reports
path: drift-reports/
GitLab CI¶
.gitlab-ci.yml:
stages:
- validate
- review
- deploy
variables:
PYTHON_VERSION: "3.11"
.install_deps: &install_deps
before_script:
- pip install hier-config-cli
validate:ios:
stage: validate
image: python:${PYTHON_VERSION}
<<: *install_deps
script:
- mkdir -p output/remediation output/rollback
- |
for config in configs/intended/ios/*.conf; do
device=$(basename "$config" .conf)
echo "Validating $device..."
hier-config-cli remediation \
--platform ios \
--running-config "configs/running/ios/${device}.conf" \
--generated-config "$config" \
--output "output/remediation/${device}.txt"
hier-config-cli rollback \
--platform ios \
--running-config "configs/running/ios/${device}.conf" \
--generated-config "$config" \
--output "output/rollback/${device}.txt"
done
artifacts:
paths:
- output/
expire_in: 1 week
only:
changes:
- configs/**
validate:nxos:
stage: validate
image: python:${PYTHON_VERSION}
<<: *install_deps
script:
- mkdir -p output/remediation output/rollback
- |
for config in configs/intended/nxos/*.conf; do
device=$(basename "$config" .conf)
echo "Validating $device..."
hier-config-cli remediation \
--platform nxos \
--running-config "configs/running/nxos/${device}.conf" \
--generated-config "$config" \
--output "output/remediation/${device}.txt"
done
artifacts:
paths:
- output/
expire_in: 1 week
only:
changes:
- configs/**
review_changes:
stage: review
image: python:${PYTHON_VERSION}
script:
- |
echo "## Configuration Changes Summary" > review.md
echo "" >> review.md
for file in output/remediation/*.txt; do
if [ -f "$file" ]; then
device=$(basename "$file" .txt)
lines=$(wc -l < "$file")
echo "- **$device**: $lines configuration lines" >> review.md
fi
done
cat review.md
artifacts:
reports:
dotenv: review.md
dependencies:
- validate:ios
- validate:nxos
only:
- merge_requests
deploy_configs:
stage: deploy
image: python:${PYTHON_VERSION}
script:
- echo "Deploying configurations..."
# Add deployment logic here
when: manual
only:
- main
dependencies:
- validate:ios
- validate:nxos
Jenkins¶
Jenkinsfile:
pipeline {
agent {
docker {
image 'python:3.11'
}
}
parameters {
choice(
name: 'PLATFORM',
choices: ['ios', 'nxos', 'iosxr', 'eos'],
description: 'Network platform to validate'
)
booleanParam(
name: 'GENERATE_ROLLBACK',
defaultValue: true,
description: 'Generate rollback configurations'
)
}
stages {
stage('Setup') {
steps {
sh 'pip install hier-config-cli'
}
}
stage('Validate Configurations') {
steps {
script {
sh '''
mkdir -p output/remediation output/rollback
for config in configs/intended/${PLATFORM}/*.conf; do
device=$(basename "$config" .conf)
echo "Validating $device..."
hier-config-cli remediation \
--platform ${PLATFORM} \
--running-config "configs/running/${PLATFORM}/${device}.conf" \
--generated-config "$config" \
--output "output/remediation/${device}.txt"
if [ "${GENERATE_ROLLBACK}" = "true" ]; then
hier-config-cli rollback \
--platform ${PLATFORM} \
--running-config "configs/running/${PLATFORM}/${device}.conf" \
--generated-config "$config" \
--output "output/rollback/${device}.txt"
fi
done
'''
}
}
}
stage('Quality Gates') {
steps {
script {
sh '''
# Check for excessive changes
for file in output/remediation/*.txt; do
if [ -f "$file" ]; then
lines=$(wc -l < "$file")
if [ "$lines" -gt 100 ]; then
echo "ERROR: Too many changes in $(basename "$file"): $lines lines"
exit 1
fi
fi
done
'''
}
}
}
stage('Generate Report') {
steps {
script {
sh '''
echo "# Configuration Validation Report" > report.md
echo "" >> report.md
echo "Platform: ${PLATFORM}" >> report.md
echo "Date: $(date)" >> report.md
echo "" >> report.md
echo "## Devices Processed" >> report.md
echo "" >> report.md
for file in output/remediation/*.txt; do
if [ -f "$file" ]; then
device=$(basename "$file" .txt)
lines=$(wc -l < "$file")
echo "- **$device**: $lines configuration lines" >> report.md
fi
done
'''
}
}
}
}
post {
always {
archiveArtifacts artifacts: 'output/**/*.txt', allowEmptyArchive: true
archiveArtifacts artifacts: 'report.md', allowEmptyArchive: true
}
success {
echo 'Configuration validation successful!'
}
failure {
echo 'Configuration validation failed!'
}
}
}
Azure DevOps¶
azure-pipelines.yml:
trigger:
branches:
include:
- main
- develop
paths:
include:
- configs/**
pool:
vmImage: 'ubuntu-latest'
variables:
pythonVersion: '3.11'
stages:
- stage: Validate
displayName: 'Validate Configurations'
jobs:
- job: ValidateIOS
displayName: 'Validate Cisco IOS Configs'
steps:
- task: UsePythonVersion@0
inputs:
versionSpec: '$(pythonVersion)'
- script: |
pip install hier-config-cli
displayName: 'Install hier-config-cli'
- script: |
mkdir -p output/remediation output/rollback
for config in configs/intended/ios/*.conf; do
device=$(basename "$config" .conf)
echo "Processing $device..."
hier-config-cli remediation \
--platform ios \
--running-config "configs/running/ios/${device}.conf" \
--generated-config "$config" \
--output "output/remediation/${device}.txt"
done
displayName: 'Generate Remediation'
- task: PublishBuildArtifacts@1
inputs:
pathToPublish: 'output'
artifactName: 'configurations'
- job: ValidateNXOS
displayName: 'Validate Cisco NX-OS Configs'
steps:
- task: UsePythonVersion@0
inputs:
versionSpec: '$(pythonVersion)'
- script: |
pip install hier-config-cli
displayName: 'Install hier-config-cli'
- script: |
mkdir -p output/remediation
for config in configs/intended/nxos/*.conf; do
device=$(basename "$config" .conf)
hier-config-cli remediation \
--platform nxos \
--running-config "configs/running/nxos/${device}.conf" \
--generated-config "$config" \
--output "output/remediation/${device}.txt"
done
displayName: 'Generate Remediation'
- stage: Deploy
displayName: 'Deploy Configurations'
dependsOn: Validate
condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/main'))
jobs:
- deployment: DeployConfigs
displayName: 'Deploy to Production'
environment: 'production'
strategy:
runOnce:
deploy:
steps:
- script: |
echo "Deploying configurations..."
# Add deployment logic here
displayName: 'Deploy'
Best Practices¶
- Run validation on every PR to catch issues early
- Use matrix builds for multi-platform environments
- Set change thresholds to prevent accidental large deployments
- Generate rollback configs automatically
- Archive artifacts for audit trails
- Use approval gates for production deployments
- Implement drift detection with scheduled jobs
- Add status badges to README for visibility
Example Status Badges¶
Add to your README.md:
Next Steps¶
- Learn about Nornir Integration
- Explore Ansible Integration
- Review Commands Reference