🚀 Ansible Advanced: Custom Modules, Dynamic Inventory, and Complex Orchestration
Advanced Ansible: Enterprise-Level Automation
In this advanced tutorial, we’ll explore sophisticated Ansible features by implementing a complex microservices deployment with custom modules and dynamic inventory management.
Prerequisites
- Strong understanding of Ansible basics and roles
- Python programming experience
- Knowledge of microservices architecture
- Familiarity with cloud platforms (AWS/GCP/Azure)
Advanced Concepts Covered
- Custom Module Development
- Dynamic Inventory Scripts
- Complex Orchestration
- Strategy Plugins
- Filter Plugins
- Callback Plugins
Working Example: Microservices Deployment
We’ll create a sophisticated deployment that:
- Uses custom modules for specialized tasks
- Implements dynamic inventory for cloud resources
- Orchestrates multiple interdependent services
- Includes advanced monitoring and logging
1. Custom Module Development
Create a custom module for service health checks:
#!/usr/bin/python
# health_check.py
from ansible.module_utils.basic import AnsibleModule
import requests
import json
def check_service_health(url, timeout=5):
try:
response = requests.get(url, timeout=timeout)
return {
'status': response.status_code,
'response_time': response.elapsed.total_seconds(),
'healthy': response.status_code in range(200, 300)
}
except requests.RequestException as e:
return {
'status': None,
'error': str(e),
'healthy': False
}
def main():
module = AnsibleModule(
argument_spec=dict(
url=dict(required=True, type='str'),
timeout=dict(type='int', default=5)
)
)
result = check_service_health(
module.params['url'],
module.params['timeout']
)
if result['healthy']:
module.exit_json(changed=False, **result)
else:
module.fail_json(msg="Service health check failed", **result)
if __name__ == '__main__':
main()
2. Dynamic Inventory Script
Create an AWS dynamic inventory script:
#!/usr/bin/env python3
# aws_dynamic_inventory.py
import boto3
import json
import argparse
class AWSInventory:
def __init__(self):
self.inventory = {}
self.ec2 = boto3.client('ec2')
def populate(self):
self.inventory = {
'_meta': {'hostvars': {}},
'all': {'children': ['ungrouped']},
'ungrouped': {'hosts': []}
}
# Get all EC2 instances
instances = self.ec2.describe_instances()
for reservation in instances['Reservations']:
for instance in reservation['Instances']:
# Skip terminated instances
if instance['State']['Name'] == 'terminated':
continue
# Get instance details
instance_id = instance['InstanceId']
private_ip = instance.get('PrivateIpAddress', '')
# Get tags
tags = {t['Key']: t['Value'] for t in instance.get('Tags', [])}
# Group by environment
env = tags.get('Environment', 'development')
if env not in self.inventory:
self.inventory[env] = {'hosts': []}
self.inventory[env]['hosts'].append(instance_id)
# Add host variables
self.inventory['_meta']['hostvars'][instance_id] = {
'ansible_host': private_ip,
'instance_type': instance['InstanceType'],
'tags': tags
}
def json(self):
return json.dumps(self.inventory, indent=2)
def main():
parser = argparse.ArgumentParser()
parser.add_argument('--list', action='store_true')
parser.add_argument('--host', action='store')
args = parser.parse_args()
inventory = AWSInventory()
inventory.populate()
print(inventory.json())
if __name__ == '__main__':
main()
3. Complex Orchestration Playbook
Create a sophisticated deployment playbook:
---
- name: Prepare Infrastructure
hosts: localhost
gather_facts: false
tasks:
- name: Create network resources
include_role:
name: network_setup
vars:
vpc_cidr: "10.0.0.0/16"
environment: ""
- name: Deploy Database Tier
hosts: tag_Role_database
become: yes
serial: 1
max_fail_percentage: 0
tasks:
- name: Include database role
include_role:
name: database
vars:
db_backup_enabled: true
db_backup_retention: 7
tags: ['database']
- name: Deploy Application Services
hosts: tag_Role_application
become: yes
strategy: free
tasks:
- name: Check system resources
include_role:
name: system_check
tags: ['precheck']
- name: Deploy microservices
include_role:
name: microservice
vars:
service_name: ""
loop:
- auth_service
- user_service
- order_service
tags: ['deploy']
- name: Health check
health_check:
url: "http://:/health"
timeout: 10
register: health_result
until: health_result is success
retries: 5
delay: 10
tags: ['healthcheck']
- name: Configure Load Balancers
hosts: tag_Role_loadbalancer
become: yes
tasks:
- name: Update backend pool
include_role:
name: loadbalancer
vars:
backend_hosts: ""
4. Custom Filter Plugin
Create a filter for processing service configurations:
# filter_plugins/service_filters.py
class FilterModule:
def filters(self):
return {
'format_service_url': self.format_service_url,
'parse_health_check': self.parse_health_check
}
def format_service_url(self, hostname, port, protocol='http'):
return f"{protocol}://{hostname}:{port}"
def parse_health_check(self, health_result):
if not health_result:
return False
return {
'is_healthy': health_result.get('status', 0) in range(200, 300),
'response_time': health_result.get('response_time', 0),
'last_check': health_result.get('timestamp')
}
5. Callback Plugin
Create a custom callback for Slack notifications:
# callback_plugins/slack_notify.py
from ansible.plugins.callback import CallbackBase
import requests
import json
class CallbackModule(CallbackBase):
CALLBACK_VERSION = 2.0
CALLBACK_TYPE = 'notification'
CALLBACK_NAME = 'slack_notify'
def __init__(self):
super(CallbackModule, self).__init__()
self.webhook_url = None
def v2_playbook_on_start(self, playbook):
self.webhook_url = self.get_option('webhook_url')
self.send_notification(
f"Starting deployment: {playbook._file_name}"
)
def v2_playbook_on_stats(self, stats):
summary = self.process_stats(stats)
self.send_notification(
f"Deployment completed\n{json.dumps(summary, indent=2)}"
)
def process_stats(self, stats):
return {
'ok': stats.ok,
'failures': stats.failures,
'skipped': stats.skipped,
'changed': stats.changed
}
def send_notification(self, message):
if not self.webhook_url:
return
payload = {
'text': message,
'username': 'Ansible Deployment'
}
try:
requests.post(
self.webhook_url,
json=payload
)
except Exception as e:
self._display.warning(
f"Failed to send Slack notification: {str(e)}"
)
Advanced Features Demonstrated
- Custom Module Features:
- Parameter validation
- Error handling
- Return value structure
- Idempotency checks
- Dynamic Inventory Capabilities:
- Cloud resource discovery
- Custom grouping logic
- Host variable management
- Caching support
- Complex Orchestration:
- Serial execution
- Failure handling
- Custom strategies
- Conditional deployment
Best Practices
- Module Development:
- Follow Ansible module guidelines
- Include comprehensive documentation
- Implement proper error handling
- Add unit tests
- Inventory Management:
- Cache results for performance
- Handle API rate limits
- Implement error recovery
- Use meaningful grouping
- Deployment Strategy:
- Plan for rollbacks
- Implement circuit breakers
- Monitor deployment health
- Use canary deployments
Debugging and Troubleshooting
- Debug custom modules:
ANSIBLE_DEBUG=1 ansible-playbook site.yml
- Test dynamic inventory:
./aws_dynamic_inventory.py --list
- Validate complex playbooks:
ansible-playbook site.yml --check --diff
Conclusion
You’ve now explored advanced Ansible features and learned how to implement complex automation solutions. This knowledge will help you build sophisticated, enterprise-level automation systems.
Remember to:
- Test thoroughly in staging
- Document custom components
- Monitor deployment metrics
- Plan for failure scenarios
The complete code for this tutorial is available in my GitHub repository: Advanced-Ansible-Examples