Configuration Management Automation with Ansible and SaltStack: Enterprise Infrastructure Framework 2026
Configuration management automation represents a cornerstone of modern infrastructure operations, enabling consistent, repeatable, and scalable system configuration across diverse enterprise environments. This comprehensive guide explores advanced Ansible and SaltStack implementations, enterprise orchestration patterns, and production-ready automation frameworks for managing complex infrastructure at scale.
Enterprise Configuration Management Architecture
Advanced Infrastructure Automation Strategy
Modern configuration management requires sophisticated orchestration capabilities that integrate with existing infrastructure, provide compliance validation, and enable rapid deployment across heterogeneous environments while maintaining security and operational standards.
Comprehensive Configuration Management Architecture
┌─────────────────────────────────────────────────────────────────┐
│ Enterprise Configuration Management Platform │
├─────────────────┬─────────────────┬─────────────────┬───────────┤
│ Inventory │ Orchestration │ Execution │ Validation│
│ Management │ Engine │ Framework │ & Audit │
├─────────────────┼─────────────────┼─────────────────┼───────────┤
│ ┌─────────────┐ │ ┌─────────────┐ │ ┌─────────────┐ │ ┌───────┐ │
│ │ Dynamic │ │ │ Ansible │ │ │ SSH/WinRM │ │ │ Tests │ │
│ │ Inventory │ │ │ Playbooks │ │ │ Agent-based │ │ │ Compliance│ │
│ │ CMDB │ │ │ Salt States │ │ │ Push/Pull │ │ │ Reporting│ │
│ │ Discovery │ │ │ Workflows │ │ │ Parallel │ │ │ Audit │ │
│ └─────────────┘ │ └─────────────┘ │ └─────────────┘ │ └───────┘ │
│ │ │ │ │
│ • Auto-discover │ • Templates │ • Multi-platform│ • Policy │
│ • Grouping │ • Variables │ • Idempotent │ • Drift │
│ • Tagging │ • Conditionals │ • Error Handling│ • Remediate│
└─────────────────┴─────────────────┴─────────────────┴───────────┘
Advanced Ansible Enterprise Configuration
# ansible-enterprise-config/ansible.cfg
[defaults]
# Inventory configuration
inventory = inventories/production/hosts.yml
host_key_checking = False
timeout = 30
gathering = smart
gather_subset = all
fact_caching = redis
fact_caching_connection = redis.monitoring.svc.cluster.local:6379:0
fact_caching_timeout = 86400
# Performance optimizations
forks = 50
serial = 30%
poll_interval = 1
internal_poll_interval = 0.001
host_key_checking = False
pipelining = True
accelerate = False
# SSH configuration
ssh_args = -C -o ControlMaster=auto -o ControlPersist=60s -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no
ssh_executable = /usr/bin/ssh
scp_if_ssh = smart
sftp_if_ssh = smart
# Logging and debugging
log_path = /var/log/ansible/ansible.log
display_skipped_hosts = False
display_ok_hosts = True
callback_whitelist = timer, profile_tasks, log_plays, mail, slack
stdout_callback = yaml
bin_ansible_callbacks = True
# Security settings
vault_password_file = /etc/ansible/vault_pass
vault_identity_list = dev@/etc/ansible/vault_pass_dev, prod@/etc/ansible/vault_pass_prod
ask_vault_pass = False
become_ask_pass = False
# Plugin settings
library = /usr/share/ansible/plugins/modules:/opt/ansible/library
module_utils = /usr/share/ansible/plugins/module_utils:/opt/ansible/module_utils
action_plugins = /usr/share/ansible/plugins/action:/opt/ansible/action_plugins
filter_plugins = /usr/share/ansible/plugins/filter:/opt/ansible/filter_plugins
lookup_plugins = /usr/share/ansible/plugins/lookup:/opt/ansible/lookup_plugins
callback_plugins = /usr/share/ansible/plugins/callback:/opt/ansible/callback_plugins
connection_plugins = /usr/share/ansible/plugins/connection:/opt/ansible/connection_plugins
strategy_plugins = /usr/share/ansible/plugins/strategy:/opt/ansible/strategy_plugins
[inventory]
enable_plugins = ini, yaml, auto, script, advanced_host_list
host_pattern_mismatch = warning
cache = True
cache_plugin = redis
cache_connection = redis.monitoring.svc.cluster.local:6379:1
cache_timeout = 3600
[privilege_escalation]
become = True
become_method = sudo
become_user = root
become_ask_pass = False
[ssh_connection]
ssh_args = -C -o ControlMaster=auto -o ControlPersist=60s
pipelining = True
control_path = /tmp/ansible-ssh-%%h-%%p-%%r
Enterprise Ansible Playbook Structure
# playbooks/site.yml - Master orchestration playbook
---
- name: Enterprise Infrastructure Configuration
hosts: localhost
gather_facts: false
vars:
deployment_timestamp: "{{ ansible_date_time.epoch }}"
environment: "{{ target_environment | default('production') }}"
pre_tasks:
- name: Validate environment
assert:
that:
- target_environment is defined
- target_environment in ['development', 'staging', 'production']
fail_msg: "Invalid or undefined target_environment"
- name: Load environment-specific variables
include_vars: "vars/{{ environment }}.yml"
- name: Verify prerequisites
include_tasks: tasks/prerequisites.yml
tasks:
- name: Configure infrastructure components
include: "{{ item }}"
loop:
- infrastructure.yml
- security.yml
- applications.yml
- monitoring.yml
when: component_enabled[item.split('.')[0]] | default(true)
---
# playbooks/infrastructure.yml
- name: Infrastructure Configuration Management
hosts: all
become: yes
strategy: linear
max_fail_percentage: 5
serial: "{{ batch_size | default('30%') }}"
vars:
infrastructure_config:
base:
packages: "{{ base_packages[ansible_os_family] }}"
services: "{{ base_services[ansible_os_family] }}"
users: "{{ infrastructure_users }}"
security:
firewall_enabled: true
selinux_state: "{{ selinux_policy[environment] }}"
ssh_hardening: true
monitoring:
agents: ['node_exporter', 'filebeat', 'osquery']
pre_tasks:
- name: Gather system facts
setup:
gather_subset:
- hardware
- network
- virtual
- pkg_mgr
tags: always
- name: Validate system compatibility
assert:
that:
- ansible_distribution in supported_distributions
- ansible_distribution_version is version(minimum_versions[ansible_distribution], '>=')
fail_msg: "Unsupported system: {{ ansible_distribution }} {{ ansible_distribution_version }}"
tags: validation
roles:
- role: common
tags: [common, base]
vars:
common_packages: "{{ infrastructure_config.base.packages }}"
common_services: "{{ infrastructure_config.base.services }}"
- role: security
tags: [security, hardening]
vars:
security_config: "{{ infrastructure_config.security }}"
- role: monitoring
tags: [monitoring, observability]
vars:
monitoring_agents: "{{ infrastructure_config.monitoring.agents }}"
when: enable_monitoring | default(true)
- role: backup
tags: [backup, data-protection]
when: enable_backup | default(true)
post_tasks:
- name: Validate configuration
include_tasks: tasks/validation.yml
tags: [validation, always]
- name: Register completion
uri:
url: "{{ config_management_api }}/completed"
method: POST
body_format: json
body:
host: "{{ inventory_hostname }}"
timestamp: "{{ deployment_timestamp }}"
status: "completed"
roles: "{{ ansible_run_tags }}"
delegate_to: localhost
tags: always
---
# roles/security/tasks/main.yml
- name: Configure system security hardening
block:
- name: Configure SSH hardening
template:
src: sshd_config.j2
dest: /etc/ssh/sshd_config
backup: yes
validate: 'sshd -t -f %s'
notify: restart sshd
tags: ssh
- name: Configure firewall rules
firewalld:
service: "{{ item.service | default(omit) }}"
port: "{{ item.port | default(omit) }}"
source: "{{ item.source | default(omit) }}"
zone: "{{ item.zone | default('public') }}"
permanent: yes
state: "{{ item.state | default('enabled') }}"
immediate: yes
loop: "{{ firewall_rules }}"
when: ansible_os_family == "RedHat"
tags: firewall
- name: Configure SELinux
selinux:
policy: targeted
state: "{{ security_config.selinux_state }}"
when: ansible_os_family == "RedHat"
tags: selinux
- name: Install and configure fail2ban
package:
name: fail2ban
state: present
notify: start fail2ban
tags: fail2ban
- name: Configure fail2ban jails
template:
src: jail.local.j2
dest: /etc/fail2ban/jail.local
notify: restart fail2ban
tags: fail2ban
- name: Configure audit rules
template:
src: audit.rules.j2
dest: /etc/audit/rules.d/ansible.rules
notify: restart auditd
tags: audit
- name: Set file permissions
file:
path: "{{ item.path }}"
mode: "{{ item.mode }}"
owner: "{{ item.owner | default('root') }}"
group: "{{ item.group | default('root') }}"
loop: "{{ security_file_permissions }}"
tags: permissions
rescue:
- name: Security configuration failed
fail:
msg: "Security hardening failed: {{ ansible_failed_result.msg }}"
when: fail_on_security_errors | default(true)
---
# Dynamic inventory script - inventories/production/inventory.py
#!/usr/bin/env python3
import json
import boto3
import requests
import argparse
from typing import Dict, List, Any
class EnterpriseInventory:
"""Advanced dynamic inventory for enterprise environments."""
def __init__(self):
self.inventory = {
'_meta': {
'hostvars': {}
}
}
self.aws_client = boto3.client('ec2')
self.cmdb_url = "https://cmdb.company.com/api/v1"
def get_aws_inventory(self) -> Dict[str, Any]:
"""Get inventory from AWS EC2."""
aws_inventory = {}
try:
response = self.aws_client.describe_instances(
Filters=[
{'Name': 'instance-state-name', 'Values': ['running']},
{'Name': 'tag:Environment', 'Values': ['production', 'staging']}
]
)
for reservation in response['Reservations']:
for instance in reservation['Instances']:
# Extract instance details
instance_id = instance['InstanceId']
private_ip = instance['PrivateIpAddress']
tags = {tag['Key']: tag['Value'] for tag in instance.get('Tags', [])}
# Group by tags
environment = tags.get('Environment', 'unknown')
role = tags.get('Role', 'unknown')
team = tags.get('Team', 'unknown')
# Create groups
for group_name in [f"env_{environment}", f"role_{role}", f"team_{team}"]:
if group_name not in aws_inventory:
aws_inventory[group_name] = {'hosts': [], 'vars': {}}
aws_inventory[group_name]['hosts'].append(private_ip)
# Add host variables
self.inventory['_meta']['hostvars'][private_ip] = {
'instance_id': instance_id,
'instance_type': instance['InstanceType'],
'availability_zone': instance['Placement']['AvailabilityZone'],
'security_groups': [sg['GroupName'] for sg in instance['SecurityGroups']],
'tags': tags,
'ansible_host': private_ip,
'ansible_user': self._get_ansible_user(tags.get('OS', 'linux'))
}
except Exception as e:
print(f"Error fetching AWS inventory: {e}")
return aws_inventory
def get_cmdb_inventory(self) -> Dict[str, Any]:
"""Get inventory from CMDB."""
cmdb_inventory = {}
try:
response = requests.get(
f"{self.cmdb_url}/hosts",
headers={'Authorization': f'Bearer {self._get_cmdb_token()}'},
timeout=30
)
if response.status_code == 200:
hosts_data = response.json()
for host in hosts_data.get('hosts', []):
hostname = host['hostname']
attributes = host['attributes']
# Create groups based on attributes
for attr_name, attr_value in attributes.items():
group_name = f"{attr_name}_{attr_value}"
if group_name not in cmdb_inventory:
cmdb_inventory[group_name] = {'hosts': [], 'vars': {}}
cmdb_inventory[group_name]['hosts'].append(hostname)
# Add host variables
self.inventory['_meta']['hostvars'][hostname] = {
'cmdb_id': host['id'],
'os_family': attributes.get('os_family'),
'datacenter': attributes.get('datacenter'),
'environment': attributes.get('environment'),
'team': attributes.get('team'),
'backup_policy': attributes.get('backup_policy'),
'monitoring_enabled': attributes.get('monitoring_enabled', True),
'ansible_host': hostname,
'ansible_user': self._get_ansible_user(attributes.get('os_family', 'linux'))
}
except Exception as e:
print(f"Error fetching CMDB inventory: {e}")
return cmdb_inventory
def _get_ansible_user(self, os_type: str) -> str:
"""Get appropriate ansible user based on OS type."""
user_mapping = {
'linux': 'ubuntu',
'redhat': 'ec2-user',
'centos': 'centos',
'windows': 'Administrator'
}
return user_mapping.get(os_type.lower(), 'ubuntu')
def _get_cmdb_token(self) -> str:
"""Get CMDB authentication token."""
# Implementation would fetch token from secure storage
return "cmdb-api-token"
def generate_inventory(self) -> Dict[str, Any]:
"""Generate complete inventory."""
# Merge inventories from different sources
aws_inventory = self.get_aws_inventory()
cmdb_inventory = self.get_cmdb_inventory()
# Merge groups
for source_inventory in [aws_inventory, cmdb_inventory]:
for group_name, group_data in source_inventory.items():
if group_name not in self.inventory:
self.inventory[group_name] = {'hosts': [], 'vars': {}}
self.inventory[group_name]['hosts'].extend(group_data['hosts'])
self.inventory[group_name]['vars'].update(group_data.get('vars', {}))
# Add meta groups
self.inventory['all'] = {
'children': list(self.inventory.keys()),
'vars': {
'ansible_ssh_common_args': '-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null',
'ansible_python_interpreter': '/usr/bin/python3'
}
}
return self.inventory
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('--list', action='store_true', help='List all groups and hosts')
parser.add_argument('--host', help='Get variables for specific host')
args = parser.parse_args()
inventory = EnterpriseInventory()
if args.list:
print(json.dumps(inventory.generate_inventory(), indent=2))
elif args.host:
# Return host-specific variables
full_inventory = inventory.generate_inventory()
host_vars = full_inventory['_meta']['hostvars'].get(args.host, {})
print(json.dumps(host_vars, indent=2))
else:
print(json.dumps({}))
Advanced SaltStack Enterprise Configuration
# /etc/salt/master.d/enterprise.conf
# Master configuration for enterprise SaltStack deployment
# Interface and port configuration
interface: 0.0.0.0
publish_port: 4505
ret_port: 4506
worker_threads: 20
timeout: 10
keep_jobs: 24
gather_job_timeout: 10
# Security configuration
open_mode: False
auto_accept: False
autosign_timeout: 120
autosign_grains_dir: /etc/salt/autosign_grains
permissive_pki_access_file: /etc/salt/autosign_file
key_logfile: /var/log/salt/key.log
# Authentication and authorization
external_auth:
ldap:
company.com:
- .*
- '@runner'
- '@wheel'
- '@jobs'
pam:
saltadmin:
- .*
- '@runner'
- '@wheel'
# Client ACL configuration
client_acl:
salt-admin:
- .*
developers:
- test.*
- grains.*
- pillar.*
operators:
- state.*
- pkg.*
- service.*
- cmd.run
# File server configuration
fileserver_backend:
- roots
- git
- s3fs
file_roots:
base:
- /srv/salt/base
- /srv/salt/shared
development:
- /srv/salt/dev
- /srv/salt/shared
staging:
- /srv/salt/staging
- /srv/salt/shared
production:
- /srv/salt/prod
- /srv/salt/shared
# Git file server backend
gitfs_remotes:
- https://github.com/company/salt-states.git:
- branch: master
- env: base
- https://github.com/company/salt-formulas.git:
- branch: master
- env: base
# S3 file server backend
s3fs_buckets:
salt-states-bucket:
keyid: AKIA...
key: xxxxx
region: us-west-2
service_url: s3.amazonaws.com
# Pillar configuration
pillar_roots:
base:
- /srv/pillar/base
- /srv/pillar/shared
development:
- /srv/pillar/dev
- /srv/pillar/shared
staging:
- /srv/pillar/staging
- /srv/pillar/shared
production:
- /srv/pillar/prod
- /srv/pillar/shared
# External pillar sources
ext_pillar:
- vault: secret/salt/{minion} profile=salt_role
- consul: consul.company.com:8500
- etcd: etcd.company.com:2379
# Reactor configuration
reactor:
- 'salt/auth':
- /srv/reactor/auth.sls
- 'salt/minion/*/start':
- /srv/reactor/minion_start.sls
- 'custom/deploy/web':
- /srv/reactor/deploy_web.sls
# Event system configuration
event_return: mysql
event_return_queue: 0
event_return_whitelist:
- salt/job/*/ret/*
- salt/cmd/*/ret/*
# Job cache configuration
job_cache: True
master_job_cache: mysql
mysql.host: mysql.company.com
mysql.user: salt
mysql.password: saltpass
mysql.db: salt
mysql.port: 3306
# Logging configuration
log_level: info
log_file: /var/log/salt/master
log_rotate_max_bytes: 104857600
log_rotate_backup_count: 5
log_fmt_console: '[%(levelname)-8s] %(message)s'
log_fmt_logfile: '%(asctime)s,%(msecs)03d [%(name)-17s][%(levelname)-8s] %(message)s'
# Performance and scaling
presence_events: True
state_events: True
start_floscript: /usr/local/bin/startup_script.py
max_minions: 10000
con_cache: True
ping_on_rotate: True
tcp_keepalive: True
tcp_keepalive_idle: 300
tcp_keepalive_cnt: 3
tcp_keepalive_intvl: 30
# Cluster configuration for HA
cluster_mode: True
cluster_masters:
- salt-master-1.company.com
- salt-master-2.company.com
- salt-master-3.company.com
This comprehensive configuration management automation guide provides enterprise-ready patterns for advanced Ansible and SaltStack implementations, enabling organizations to achieve consistent, secure, and scalable infrastructure automation at enterprise scale.
Key benefits of this advanced configuration management approach include:
- Scalable Automation: Enterprise-grade configuration management for large-scale environments
- Security Integration: Built-in security hardening and compliance validation
- Multi-Platform Support: Unified automation across diverse operating systems and platforms
- Advanced Orchestration: Sophisticated workflow automation with error handling and rollback
- Compliance Management: Automated policy enforcement and audit trail generation
- Operational Excellence: Monitoring, alerting, and reporting for configuration management operations
The implementation patterns demonstrated here enable organizations to achieve operational excellence through comprehensive automation while maintaining security and compliance standards.