Advanced Firewall Automation and Configuration Management: Enterprise Security Guide
Advanced firewall automation and configuration management enable enterprises to maintain consistent security policies, reduce human error, and scale security operations effectively. This comprehensive guide explores automated firewall management, policy orchestration, and production-ready frameworks for enterprise security environments.
Enterprise Firewall Automation
Section 1: Multi-Vendor Firewall Management Framework
Modern enterprises require sophisticated automation frameworks that can manage diverse firewall platforms while maintaining consistent security policies.
Universal Firewall Management System
from abc import ABC, abstractmethod
from typing import Dict, List, Any, Optional
from dataclasses import dataclass, field
from enum import Enum
import asyncio
import yaml
import json
import logging
from datetime import datetime, timedelta
class FirewallVendor(Enum):
CISCO_ASA = "cisco_asa"
PALO_ALTO = "palo_alto"
FORTINET = "fortinet"
CHECKPOINT = "checkpoint"
JUNIPER_SRX = "juniper_srx"
SONICWALL = "sonicwall"
PFsense = "pfense"
@dataclass
class SecurityRule:
name: str
source_zones: List[str]
destination_zones: List[str]
source_addresses: List[str]
destination_addresses: List[str]
services: List[str]
action: str
log_enabled: bool = True
enabled: bool = True
description: str = ""
tags: List[str] = field(default_factory=list)
created_date: datetime = field(default_factory=datetime.now)
modified_date: datetime = field(default_factory=datetime.now)
@dataclass
class FirewallDevice:
name: str
vendor: FirewallVendor
management_ip: str
api_endpoint: str
credentials: Dict[str, str]
zones: List[str]
interfaces: List[str]
version: str
model: str
cluster_member: bool = False
cluster_id: Optional[str] = None
class FirewallConnector(ABC):
"""Abstract base class for firewall connectors"""
@abstractmethod
async def connect(self) -> bool:
pass
@abstractmethod
async def disconnect(self) -> bool:
pass
@abstractmethod
async def get_rules(self, policy_name: str = None) -> List[SecurityRule]:
pass
@abstractmethod
async def add_rule(self, rule: SecurityRule, position: int = None) -> bool:
pass
@abstractmethod
async def update_rule(self, rule_id: str, rule: SecurityRule) -> bool:
pass
@abstractmethod
async def delete_rule(self, rule_id: str) -> bool:
pass
@abstractmethod
async def commit_changes(self) -> bool:
pass
@abstractmethod
async def backup_configuration(self) -> str:
pass
class EnterpriseFirewallManager:
def __init__(self, config_file: str = None):
self.devices = {}
self.connectors = {}
self.policy_engine = PolicyEngine()
self.compliance_checker = FirewallComplianceChecker()
self.change_tracker = FirewallChangeTracker()
self.orchestrator = PolicyOrchestrator()
self.logger = self._setup_logging()
if config_file:
self.load_configuration(config_file)
def _setup_logging(self):
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
handlers=[
logging.FileHandler('firewall_automation.log'),
logging.StreamHandler()
]
)
return logging.getLogger(__name__)
def register_device(self, device: FirewallDevice):
"""Register firewall device for management"""
self.devices[device.name] = device
# Create appropriate connector
if device.vendor == FirewallVendor.CISCO_ASA:
connector = CiscoASAConnector(device)
elif device.vendor == FirewallVendor.PALO_ALTO:
connector = PaloAltoConnector(device)
elif device.vendor == FirewallVendor.FORTINET:
connector = FortinetConnector(device)
elif device.vendor == FirewallVendor.CHECKPOINT:
connector = CheckPointConnector(device)
elif device.vendor == FirewallVendor.JUNIPER_SRX:
connector = JuniperSRXConnector(device)
else:
raise ValueError(f"Unsupported firewall vendor: {device.vendor}")
self.connectors[device.name] = connector
self.logger.info(f"Registered firewall device: {device.name}")
async def deploy_policy_set(self, policy_set: Dict[str, Any],
target_devices: List[str] = None) -> Dict[str, Any]:
"""Deploy comprehensive policy set across multiple devices"""
if target_devices is None:
target_devices = list(self.devices.keys())
deployment_results = {}
# Pre-deployment validation
validation_results = await self._validate_policy_set(policy_set, target_devices)
if not validation_results['valid']:
return {
'success': False,
'error': 'Policy validation failed',
'validation_errors': validation_results['errors']
}
# Execute deployment across devices
deployment_tasks = []
for device_name in target_devices:
task = self._deploy_to_device(device_name, policy_set)
deployment_tasks.append(task)
results = await asyncio.gather(*deployment_tasks, return_exceptions=True)
# Process results
for i, result in enumerate(results):
device_name = target_devices[i]
if isinstance(result, Exception):
deployment_results[device_name] = {
'success': False,
'error': str(result)
}
else:
deployment_results[device_name] = result
# Generate deployment summary
summary = self._generate_deployment_summary(deployment_results)
return {
'success': summary['overall_success'],
'summary': summary,
'device_results': deployment_results,
'deployment_id': self.change_tracker.create_deployment_record(
policy_set, target_devices, deployment_results
)
}
async def _deploy_to_device(self, device_name: str,
policy_set: Dict[str, Any]) -> Dict[str, Any]:
"""Deploy policy set to individual device"""
connector = self.connectors[device_name]
device = self.devices[device_name]
try:
# Connect to device
if not await connector.connect():
return {'success': False, 'error': 'Connection failed'}
# Backup current configuration
backup_id = await self._create_backup(device_name)
# Translate policy set to device-specific configuration
device_config = await self.orchestrator.translate_policy_set(
policy_set, device.vendor
)
deployment_result = {
'success': True,
'backup_id': backup_id,
'changes': [],
'errors': []
}
# Deploy security rules
if 'security_rules' in device_config:
for rule in device_config['security_rules']:
try:
rule_obj = SecurityRule(**rule)
await connector.add_rule(rule_obj)
deployment_result['changes'].append(f"Added rule: {rule['name']}")
except Exception as e:
deployment_result['errors'].append(f"Failed to add rule {rule['name']}: {e}")
# Deploy NAT rules
if 'nat_rules' in device_config:
for nat_rule in device_config['nat_rules']:
try:
await connector.add_nat_rule(nat_rule)
deployment_result['changes'].append(f"Added NAT rule: {nat_rule['name']}")
except Exception as e:
deployment_result['errors'].append(f"Failed to add NAT rule {nat_rule['name']}: {e}")
# Deploy network objects
if 'network_objects' in device_config:
for obj in device_config['network_objects']:
try:
await connector.create_network_object(obj)
deployment_result['changes'].append(f"Created object: {obj['name']}")
except Exception as e:
deployment_result['errors'].append(f"Failed to create object {obj['name']}: {e}")
# Commit changes
if deployment_result['changes'] and not deployment_result['errors']:
if await connector.commit_changes():
self.logger.info(f"Successfully deployed to {device_name}")
else:
deployment_result['success'] = False
deployment_result['errors'].append("Failed to commit changes")
elif deployment_result['errors']:
deployment_result['success'] = False
return deployment_result
except Exception as e:
self.logger.error(f"Deployment failed for {device_name}: {e}")
return {'success': False, 'error': str(e)}
finally:
await connector.disconnect()
async def automated_compliance_check(self, compliance_rules: Dict[str, Any],
target_devices: List[str] = None) -> Dict[str, Any]:
"""Perform automated compliance checking across devices"""
if target_devices is None:
target_devices = list(self.devices.keys())
compliance_results = {}
# Execute compliance checks
compliance_tasks = []
for device_name in target_devices:
task = self._check_device_compliance(device_name, compliance_rules)
compliance_tasks.append(task)
results = await asyncio.gather(*compliance_tasks, return_exceptions=True)
# Process results
for i, result in enumerate(results):
device_name = target_devices[i]
if isinstance(result, Exception):
compliance_results[device_name] = {
'compliant': False,
'error': str(result)
}
else:
compliance_results[device_name] = result
# Generate compliance report
report = self.compliance_checker.generate_compliance_report(compliance_results)
return {
'overall_compliance': report['overall_compliance'],
'compliance_score': report['compliance_score'],
'device_results': compliance_results,
'recommendations': report['recommendations'],
'report_id': report['report_id']
}
async def _check_device_compliance(self, device_name: str,
compliance_rules: Dict[str, Any]) -> Dict[str, Any]:
"""Check compliance for individual device"""
connector = self.connectors[device_name]
device = self.devices[device_name]
try:
if not await connector.connect():
return {'compliant': False, 'error': 'Connection failed'}
# Get current configuration
current_rules = await connector.get_rules()
current_config = await connector.get_configuration()
# Perform compliance checks
compliance_result = self.compliance_checker.check_compliance(
device, current_rules, current_config, compliance_rules
)
return compliance_result
except Exception as e:
self.logger.error(f"Compliance check failed for {device_name}: {e}")
return {'compliant': False, 'error': str(e)}
finally:
await connector.disconnect()
class PolicyEngine:
"""Advanced policy management and optimization engine"""
def __init__(self):
self.policy_templates = {}
self.rule_optimizer = RuleOptimizer()
self.conflict_detector = PolicyConflictDetector()
def create_policy_from_template(self, template_name: str,
parameters: Dict[str, Any]) -> Dict[str, Any]:
"""Create policy from predefined template"""
if template_name not in self.policy_templates:
raise ValueError(f"Template {template_name} not found")
template = self.policy_templates[template_name]
policy = self._render_template(template, parameters)
# Optimize rules
optimized_policy = self.rule_optimizer.optimize_policy(policy)
# Check for conflicts
conflicts = self.conflict_detector.detect_conflicts(optimized_policy)
if conflicts:
raise ValueError(f"Policy conflicts detected: {conflicts}")
return optimized_policy
def analyze_policy_impact(self, policy: Dict[str, Any],
existing_policies: List[Dict[str, Any]]) -> Dict[str, Any]:
"""Analyze impact of new policy on existing policies"""
impact_analysis = {
'rule_conflicts': [],
'shadowed_rules': [],
'redundant_rules': [],
'performance_impact': {},
'security_gaps': []
}
# Detect rule conflicts
for existing_policy in existing_policies:
conflicts = self.conflict_detector.find_conflicts(policy, existing_policy)
impact_analysis['rule_conflicts'].extend(conflicts)
# Identify shadowed rules
shadowed = self.rule_optimizer.find_shadowed_rules(policy, existing_policies)
impact_analysis['shadowed_rules'].extend(shadowed)
# Calculate performance impact
impact_analysis['performance_impact'] = self._calculate_performance_impact(
policy, existing_policies
)
return impact_analysis
class RuleOptimizer:
"""Optimize firewall rules for performance and maintainability"""
def optimize_policy(self, policy: Dict[str, Any]) -> Dict[str, Any]:
"""Comprehensive policy optimization"""
optimized_policy = policy.copy()
# Remove redundant rules
optimized_policy = self._remove_redundant_rules(optimized_policy)
# Consolidate similar rules
optimized_policy = self._consolidate_rules(optimized_policy)
# Optimize rule ordering
optimized_policy = self._optimize_rule_order(optimized_policy)
# Remove unused objects
optimized_policy = self._remove_unused_objects(optimized_policy)
return optimized_policy
def _remove_redundant_rules(self, policy: Dict[str, Any]) -> Dict[str, Any]:
"""Remove redundant security rules"""
rules = policy.get('security_rules', [])
unique_rules = []
for rule in rules:
if not self._is_redundant_rule(rule, unique_rules):
unique_rules.append(rule)
policy['security_rules'] = unique_rules
return policy
def _consolidate_rules(self, policy: Dict[str, Any]) -> Dict[str, Any]:
"""Consolidate similar rules into single rules"""
rules = policy.get('security_rules', [])
consolidated_rules = []
# Group rules by similarity
rule_groups = self._group_similar_rules(rules)
for group in rule_groups:
if len(group) > 1:
# Consolidate group into single rule
consolidated_rule = self._merge_rules(group)
consolidated_rules.append(consolidated_rule)
else:
consolidated_rules.extend(group)
policy['security_rules'] = consolidated_rules
return policy
def _optimize_rule_order(self, policy: Dict[str, Any]) -> Dict[str, Any]:
"""Optimize rule ordering for performance"""
rules = policy.get('security_rules', [])
# Sort rules by hit frequency and specificity
sorted_rules = sorted(rules, key=lambda r: (
-self._get_rule_hit_frequency(r),
-self._calculate_rule_specificity(r)
))
policy['security_rules'] = sorted_rules
return policy
class PolicyConflictDetector:
"""Detect and resolve policy conflicts"""
def detect_conflicts(self, policy: Dict[str, Any]) -> List[Dict[str, Any]]:
"""Detect conflicts within a policy"""
conflicts = []
rules = policy.get('security_rules', [])
for i, rule1 in enumerate(rules):
for j, rule2 in enumerate(rules[i+1:], i+1):
conflict = self._check_rule_conflict(rule1, rule2)
if conflict:
conflicts.append({
'type': conflict['type'],
'rule1_index': i,
'rule2_index': j,
'rule1_name': rule1.get('name', f'Rule_{i}'),
'rule2_name': rule2.get('name', f'Rule_{j}'),
'description': conflict['description'],
'severity': conflict['severity']
})
return conflicts
def _check_rule_conflict(self, rule1: Dict[str, Any],
rule2: Dict[str, Any]) -> Optional[Dict[str, Any]]:
"""Check if two rules conflict"""
# Check for shadowing
if self._is_rule_shadowed(rule1, rule2):
return {
'type': 'shadowing',
'description': f"Rule '{rule1['name']}' is shadowed by '{rule2['name']}'",
'severity': 'high'
}
# Check for contradictory actions
if self._have_contradictory_actions(rule1, rule2):
return {
'type': 'contradictory_action',
'description': f"Rules have contradictory actions for same traffic",
'severity': 'critical'
}
return None
class FirewallComplianceChecker:
"""Check firewall compliance against security standards"""
def __init__(self):
self.compliance_rules = self._load_compliance_rules()
def check_compliance(self, device: FirewallDevice, rules: List[SecurityRule],
config: Dict[str, Any], custom_rules: Dict[str, Any]) -> Dict[str, Any]:
"""Comprehensive compliance checking"""
compliance_result = {
'compliant': True,
'compliance_score': 0,
'checks_performed': 0,
'checks_passed': 0,
'violations': [],
'recommendations': []
}
# Standard compliance checks
for rule_category, category_rules in self.compliance_rules.items():
category_result = self._check_category_compliance(
device, rules, config, rule_category, category_rules
)
self._merge_compliance_results(compliance_result, category_result)
# Custom compliance checks
if custom_rules:
custom_result = self._check_custom_compliance(
device, rules, config, custom_rules
)
self._merge_compliance_results(compliance_result, custom_result)
# Calculate overall compliance score
if compliance_result['checks_performed'] > 0:
compliance_result['compliance_score'] = (
compliance_result['checks_passed'] /
compliance_result['checks_performed'] * 100
)
return compliance_result
def _check_category_compliance(self, device: FirewallDevice, rules: List[SecurityRule],
config: Dict[str, Any], category: str,
category_rules: List[Dict[str, Any]]) -> Dict[str, Any]:
"""Check compliance for specific category"""
result = {
'checks_performed': 0,
'checks_passed': 0,
'violations': [],
'recommendations': []
}
for compliance_rule in category_rules:
result['checks_performed'] += 1
if self._evaluate_compliance_rule(device, rules, config, compliance_rule):
result['checks_passed'] += 1
else:
violation = {
'category': category,
'rule': compliance_rule['name'],
'severity': compliance_rule.get('severity', 'medium'),
'description': compliance_rule['description'],
'remediation': compliance_rule.get('remediation', 'No remediation provided')
}
result['violations'].append(violation)
return result
class FirewallChangeTracker:
"""Track and audit firewall changes"""
def __init__(self, audit_db_path: str = "firewall_changes.db"):
self.audit_db_path = audit_db_path
self.logger = logging.getLogger(f"{__name__}.ChangeTracker")
self._init_audit_database()
def create_deployment_record(self, policy_set: Dict[str, Any],
target_devices: List[str],
results: Dict[str, Any]) -> str:
"""Create deployment record for audit trail"""
deployment_id = self._generate_deployment_id()
deployment_record = {
'deployment_id': deployment_id,
'timestamp': datetime.now().isoformat(),
'operator': self._get_current_user(),
'policy_set': policy_set,
'target_devices': target_devices,
'results': results,
'success': all(r.get('success', False) for r in results.values())
}
self._store_deployment_record(deployment_record)
self.logger.info(f"Created deployment record: {deployment_id}")
return deployment_id
def track_rule_change(self, device_name: str, change_type: str,
rule_data: Dict[str, Any]):
"""Track individual rule changes"""
change_record = {
'change_id': self._generate_change_id(),
'timestamp': datetime.now().isoformat(),
'device_name': device_name,
'change_type': change_type,
'rule_data': rule_data,
'operator': self._get_current_user()
}
self._store_change_record(change_record)
def generate_audit_report(self, start_date: datetime,
end_date: datetime) -> Dict[str, Any]:
"""Generate comprehensive audit report"""
deployments = self._get_deployments_in_range(start_date, end_date)
changes = self._get_changes_in_range(start_date, end_date)
report = {
'report_period': {
'start_date': start_date.isoformat(),
'end_date': end_date.isoformat()
},
'summary': {
'total_deployments': len(deployments),
'successful_deployments': sum(1 for d in deployments if d['success']),
'total_changes': len(changes),
'devices_affected': len(set(c['device_name'] for c in changes))
},
'deployments': deployments,
'changes': changes,
'compliance_violations': self._get_compliance_violations_in_range(
start_date, end_date
)
}
return report
class PolicyOrchestrator:
"""Orchestrate policy deployment across multiple vendors"""
def __init__(self):
self.translators = {
FirewallVendor.CISCO_ASA: CiscoASATranslator(),
FirewallVendor.PALO_ALTO: PaloAltoTranslator(),
FirewallVendor.FORTINET: FortinetTranslator(),
FirewallVendor.CHECKPOINT: CheckPointTranslator(),
FirewallVendor.JUNIPER_SRX: JuniperSRXTranslator()
}
async def translate_policy_set(self, policy_set: Dict[str, Any],
vendor: FirewallVendor) -> Dict[str, Any]:
"""Translate universal policy set to vendor-specific configuration"""
if vendor not in self.translators:
raise ValueError(f"No translator available for vendor: {vendor}")
translator = self.translators[vendor]
return translator.translate(policy_set)
async def validate_translation(self, original_policy: Dict[str, Any],
translated_config: Dict[str, Any],
vendor: FirewallVendor) -> Dict[str, Any]:
"""Validate that translation preserves policy intent"""
translator = self.translators[vendor]
return translator.validate_translation(original_policy, translated_config)
class CiscoASATranslator:
"""Translate policies to Cisco ASA configuration"""
def translate(self, policy_set: Dict[str, Any]) -> Dict[str, Any]:
"""Translate policy set to ASA configuration"""
asa_config = {
'security_rules': [],
'nat_rules': [],
'network_objects': [],
'service_objects': []
}
# Translate security rules
for rule in policy_set.get('security_rules', []):
asa_rule = self._translate_security_rule(rule)
asa_config['security_rules'].append(asa_rule)
# Translate NAT rules
for nat_rule in policy_set.get('nat_rules', []):
asa_nat = self._translate_nat_rule(nat_rule)
asa_config['nat_rules'].append(asa_nat)
# Translate network objects
for obj in policy_set.get('network_objects', []):
asa_obj = self._translate_network_object(obj)
asa_config['network_objects'].append(asa_obj)
return asa_config
def _translate_security_rule(self, rule: Dict[str, Any]) -> Dict[str, Any]:
"""Translate security rule to ASA access-list format"""
return {
'name': rule['name'],
'line': f"access-list {rule['acl_name']} extended {rule['action']} "
f"{' '.join(rule['protocol'])} {' '.join(rule['source'])} "
f"{' '.join(rule['destination'])} {' '.join(rule['service'])}",
'enabled': rule.get('enabled', True),
'log': rule.get('log_enabled', True)
}
def _translate_nat_rule(self, nat_rule: Dict[str, Any]) -> Dict[str, Any]:
"""Translate NAT rule to ASA NAT format"""
return {
'name': nat_rule['name'],
'type': nat_rule['type'],
'configuration': f"object network {nat_rule['object_name']}\n"
f" nat ({nat_rule['inside_interface']},{nat_rule['outside_interface']}) "
f"{nat_rule['translated_address']}"
}
class PaloAltoTranslator:
"""Translate policies to Palo Alto configuration"""
def translate(self, policy_set: Dict[str, Any]) -> Dict[str, Any]:
"""Translate policy set to Palo Alto XML configuration"""
pa_config = {
'security_rules': [],
'nat_rules': [],
'address_objects': [],
'service_objects': []
}
# Translate security rules to Palo Alto security policy
for rule in policy_set.get('security_rules', []):
pa_rule = self._translate_security_rule(rule)
pa_config['security_rules'].append(pa_rule)
return pa_config
def _translate_security_rule(self, rule: Dict[str, Any]) -> Dict[str, Any]:
"""Translate security rule to Palo Alto format"""
return {
'name': rule['name'],
'from': rule['source_zones'],
'to': rule['destination_zones'],
'source': rule['source_addresses'],
'destination': rule['destination_addresses'],
'service': rule['services'],
'action': rule['action'],
'log-start': rule.get('log_enabled', True),
'log-end': rule.get('log_enabled', True),
'description': rule.get('description', ''),
'tag': rule.get('tags', [])
}
class FirewallMonitoring:
"""Advanced firewall monitoring and analytics"""
def __init__(self):
self.metrics_collector = FirewallMetricsCollector()
self.log_analyzer = FirewallLogAnalyzer()
self.performance_monitor = FirewallPerformanceMonitor()
self.threat_detector = FirewallThreatDetector()
async def collect_comprehensive_metrics(self, devices: List[str]) -> Dict[str, Any]:
"""Collect comprehensive metrics from firewall devices"""
metrics = {}
for device_name in devices:
device_metrics = await self._collect_device_metrics(device_name)
metrics[device_name] = device_metrics
# Aggregate metrics
aggregated_metrics = self._aggregate_metrics(metrics)
return {
'individual_metrics': metrics,
'aggregated_metrics': aggregated_metrics,
'collection_timestamp': datetime.now().isoformat()
}
async def _collect_device_metrics(self, device_name: str) -> Dict[str, Any]:
"""Collect metrics from individual device"""
return {
'performance': await self.performance_monitor.get_performance_metrics(device_name),
'traffic': await self.metrics_collector.get_traffic_metrics(device_name),
'security': await self.threat_detector.get_security_metrics(device_name),
'configuration': await self.metrics_collector.get_config_metrics(device_name)
}
def analyze_rule_effectiveness(self, device_name: str,
time_window: int = 3600) -> Dict[str, Any]:
"""Analyze firewall rule effectiveness"""
logs = self.log_analyzer.get_logs(device_name, time_window)
analysis = {
'rule_hit_counts': {},
'unused_rules': [],
'top_blocked_sources': [],
'top_allowed_destinations': [],
'performance_impact': {}
}
# Analyze rule usage
for log_entry in logs:
rule_name = log_entry.get('rule_name')
if rule_name:
analysis['rule_hit_counts'][rule_name] = \
analysis['rule_hit_counts'].get(rule_name, 0) + 1
# Identify unused rules
all_rules = self._get_all_rules(device_name)
for rule in all_rules:
if rule['name'] not in analysis['rule_hit_counts']:
analysis['unused_rules'].append(rule['name'])
return analysis
class FirewallReportingEngine:
"""Generate comprehensive firewall reports"""
def generate_security_posture_report(self, devices: List[str]) -> Dict[str, Any]:
"""Generate security posture report"""
report = {
'executive_summary': {},
'device_summaries': {},
'compliance_status': {},
'threat_analysis': {},
'recommendations': []
}
# Collect data for each device
for device_name in devices:
device_summary = self._generate_device_summary(device_name)
report['device_summaries'][device_name] = device_summary
# Generate executive summary
report['executive_summary'] = self._generate_executive_summary(
report['device_summaries']
)
return report
def _generate_device_summary(self, device_name: str) -> Dict[str, Any]:
"""Generate summary for individual device"""
return {
'rule_count': self._get_rule_count(device_name),
'policy_violations': self._get_policy_violations(device_name),
'performance_metrics': self._get_performance_summary(device_name),
'security_events': self._get_security_events_summary(device_name),
'last_backup': self._get_last_backup_date(device_name),
'compliance_score': self._get_compliance_score(device_name)
}
This comprehensive guide demonstrates enterprise-grade firewall automation with advanced policy management, multi-vendor support, compliance checking, and sophisticated monitoring capabilities. The examples provide production-ready patterns for implementing scalable, secure, and efficient firewall management in enterprise environments.