Skip to content

Advanced Topics

This guide covers advanced topics and techniques for using the Palo Alto Networks SCM Ansible Collection in more complex scenarios.

Dynamic Inventory Integration

Using the SCM Inventory Plugin with Terraform

Combine Terraform-managed infrastructure with SCM dynamic inventory:

# scm_terraform.yml
plugin: cdot65.scm.inventory
keyed_groups:
  - prefix: terraform_
    key: tags.Terraform
compose:
  terraform_managed: "'terraform' in (tags|default({}))"

# In playbook:
- name: Configure Terraform-managed firewalls
  hosts: terraform_true
  tasks:
    - name: Apply baseline configuration
      cdot65.scm.bootstrap:
        bootstrap_folder: "Terraform-Managed"

Hybrid Cloud Inventory

Combine multiple inventory sources for hybrid environments:

# inventory.yml
---
plugin: cdot65.scm.inventory
device_groups:
  - "Cloud-Firewalls"
compose:
  deployment_type: "'cloud'"
---
plugin: aws_ec2
regions:
  - us-east-1
  - us-west-2
filters:
  tag:Type: firewall
compose:
  deployment_type: "'aws'"

Workflow Automation

GitOps Workflow for SCM

Implement a GitOps workflow for SCM configurations:

---
- name: GitOps Workflow for SCM
  hosts: localhost
  connection: local

  vars:
    git_repo: "https://github.com/example/scm-configs.git"
    git_branch: "{{ lookup('env', 'CI_COMMIT_BRANCH') | default('main', true) }}"
    config_path: "./scm-configs"

  tasks:
    # Clone Git repository
    - name: Clone configuration repository
      git:
        repo: "{{ git_repo }}"
        dest: "{{ config_path }}"
        version: "{{ git_branch }}"

    # Load configuration from YAML files
    - name: Load configuration files
      include_vars:
        dir: "{{ config_path }}/{{ item }}"
        name: "{{ item }}"
      loop:
        - "address_objects"
        - "security_rules"
        - "service_objects"

    # Apply address objects
    - name: Apply address objects
      cdot65.scm.address:
        name: "{{ item.name }}"
        folder: "{{ item.folder }}"
        description: "{{ item.description | default(omit) }}"
        ip_netmask: "{{ item.ip_netmask | default(omit) }}"
        ip_range: "{{ item.ip_range | default(omit) }}"
        fqdn: "{{ item.fqdn | default(omit) }}"
        tags: "{{ item.tags | default(omit) }}"
      loop: "{{ address_objects.objects }}"

    # Similar tasks for other object types

    # Commit and tag changes
    - name: Commit changes
      cdot65.scm.commit:
        description: "GitOps deployment from {{ git_branch }} ({{ lookup('env', 'CI_COMMIT_SHA') | default('local', true) }})"

Multi-Step Approval Workflow

Implement approvals for security policy changes:

---
- name: Request Security Policy Change
  hosts: localhost
  connection: local

  vars:
    change_request_id: "CHG{{ lookup('pipe', 'date +%Y%m%d%H%M%S') }}"
    approver_email: "security-team@example.com"

  tasks:
    # Create candidate rule but don't commit
    - name: Create candidate security rule
      cdot65.scm.security_rule:
        name: "Rule-{{ change_request_id }}"
        folder: "Pending-Approval"
        description: "Pending approval: {{ change_request_id }}"
        source_zones: ["{{ source_zone }}"]
        destination_zones: ["{{ destination_zone }}"]
        source_addresses: ["{{ source_address }}"]
        destination_addresses: ["{{ destination_address }}"]
        applications: ["{{ application }}"]
        action: "allow"
      register: rule_result

    # Send approval request
    - name: Send approval email
      mail:
        host: smtp.example.com
        port: 25
        to: "{{ approver_email }}"
        subject: "Security Rule Approval Request: {{ change_request_id }}"
        body: |
          A new security rule requires approval:

          Rule ID: {{ rule_result.scm_object.id }}
          Name: Rule-{{ change_request_id }}
          Source: {{ source_zone }}
          Destination: {{ destination_zone }}
          Application: {{ application }}

          Approve at: https://scm.example.com/pending/{{ change_request_id }}

Advanced Deployment Strategies

Rolling Deployment

Implement a rolling deployment strategy:

---
- name: Rolling Firewall Deployment
  hosts: localhost
  connection: local

  vars:
    device_groups:
      - name: "US-East-Firewalls"
        max_parallel: 2
        wait_time: 300  # seconds
      - name: "US-West-Firewalls"
        max_parallel: 2
        wait_time: 300
      - name: "EMEA-Firewalls"
        max_parallel: 3
        wait_time: 300

  tasks:
    # Commit changes first
    - name: Commit configuration changes
      cdot65.scm.commit:
        description: "Preparing for rolling deployment"
      register: commit_result

    # Deploy to each device group in sequence
    - name: Rolling deployment to device groups
      block:
        # Get devices in the group
        - name: Get devices in group
          cdot65.scm.device_group_info:
            name: "{{ item.name }}"
          register: device_group

        # Deploy to subsets of devices
        - name: Deploy to device subset
          include_tasks: deploy_subset.yml
          vars:
            device_subset: "{{ device_group.devices[start_idx:end_idx] }}"
            wait_time: "{{ item.wait_time }}"
          loop: "{{ range(0, device_group.devices|length, item.max_parallel)|list }}"
          loop_control:
            loop_var: start_idx
          vars:
            end_idx: "{{ [start_idx + item.max_parallel, device_group.devices|length] | min }}"

      loop: "{{ device_groups }}"

Validation and Rollback

Implement validation and automatic rollback:

---
- name: Deploy with Validation and Rollback
  hosts: localhost
  connection: local

  vars:
    max_retry_attempts: 3
    validation_wait: 60  # seconds

  tasks:
    # Store current configuration version
    - name: Get current configuration version
      cdot65.scm.version_info:
        folder: "SharedFolder"
      register: current_version

    # Make changes
    - name: Apply security rule changes
      cdot65.scm.security_rule:
        name: "New-Application-Rule"
        folder: "SharedFolder"
        description: "Allow new application"
        source_zones: ["Internal"]
        destination_zones: ["External"]
        applications: ["custom-app"]
        action: "allow"
      register: rule_result

    # Commit changes
    - name: Commit changes
      cdot65.scm.commit:
        description: "New application rule deployment"
      register: commit_result

    # Deploy with validation
    - name: Deploy and validate
      block:
        # Push to test device
        - name: Push to test device
          cdot65.scm.push_config:
            devices:
              - "test-firewall.example.com"
          register: push_result

        # Wait for deployment 
        - name: Wait for deployment
          cdot65.scm.job_info:
            job_id: "{{ push_result.job_id }}"
          register: job_status
          until: job_status.status == "COMPLETED" or job_status.status == "FAILED"
          retries: 30
          delay: 10

        # Validate deployment
        - name: Validate deployment
          cdot65.scm.validate_config:
            device: "test-firewall.example.com"
            tests:
              - type: "connectivity"
                source_ip: "10.0.0.10"
                destination_ip: "8.8.8.8"
                application: "dns"
          register: validation
          failed_when: not validation.passed

      rescue:
        # Rollback on failure
        - name: Rollback to previous version
          cdot65.scm.rollback:
            folder: "SharedFolder"
            version: "{{ current_version.version }}"
          register: rollback_result

        - name: Commit rollback
          cdot65.scm.commit:
            description: "Rollback due to validation failure"

        - name: Push rollback
          cdot65.scm.push_config:
            devices:
              - "test-firewall.example.com"

        - name: Fail with rollback message
          fail:
            msg: "Deployment failed validation and was rolled back to version {{ current_version.version }}"

Custom Module Integration

Creating a Custom SCM Module Wrapper

Create custom wrapper modules for specific use cases:

#!/usr/bin/python
# custom_modules/scm_compliance_rule.py

DOCUMENTATION = '''
---
module: scm_compliance_rule
short_description: Creates pre-configured compliant security rules
description:
    - Creates security rules in SCM that comply with organizational standards
options:
    name:
        description: Rule name
        required: true
    folder:
        description: SCM folder
        required: true
    # Additional parameters...
'''

from ansible.module_utils.basic import AnsibleModule
import ansible_collections.cdot65.scm.plugins.module_utils.scm as scm_utils

def run_module():
    module_args = dict(
        name=dict(type='str', required=True),
        folder=dict(type='str', required=True),
        rule_type=dict(type='str', required=True, choices=['outbound-web', 'inbound-web', 'internal']),
        # Additional parameters...
    )

    result = dict(
        changed=False,
        scm_object=None,
    )

    module = AnsibleModule(
        argument_spec=module_args,
        supports_check_mode=True
    )

    # Map rule types to standardized configurations
    rule_templates = {
        'outbound-web': {
            'source_zones': ['Internal'],
            'destination_zones': ['Internet'],
            'applications': ['web-browsing', 'ssl'],
            'profile_type': 'profiles',
            'url_filtering_profile': 'default-url-filtering'
        },
        # Additional templates...
    }

    # Create parameters for the underlying module
    rule_params = rule_templates.get(module.params['rule_type']).copy()
    rule_params.update({
        'name': module.params['name'],
        'folder': module.params['folder'],
        'log_end': True
    })

    # Use SCM utilities to create the rule
    scm_client = scm_utils.get_scm_client(module)

    if module.check_mode:
        result['changed'] = True
        module.exit_json(**result)

    try:
        rule_result = scm_client.security_rule.create(rule_params)
        result['changed'] = True
        result['scm_object'] = rule_result
    except Exception as e:
        module.fail_json(msg=f"Error creating compliance rule: {str(e)}")

    module.exit_json(**result)

if __name__ == '__main__':
    run_module()

Integrating with External CMDB

Connect SCM with an external CMDB:

---
- name: Integrate SCM with CMDB
  hosts: localhost
  connection: local

  vars:
    cmdb_url: "https://cmdb.example.com/api"
    cmdb_token: "{{ lookup('env', 'CMDB_TOKEN') }}"

  tasks:
    # Get server information from CMDB
    - name: Query CMDB for servers
      uri:
        url: "{{ cmdb_url }}/servers"
        headers:
          Authorization: "Bearer {{ cmdb_token }}"
        return_content: yes
      register: cmdb_servers

    # Process CMDB data
    - name: Process CMDB servers
      set_fact:
        web_servers: "{{ cmdb_servers.json | json_query('[?tags.contains(@, `web`)]') }}"
        app_servers: "{{ cmdb_servers.json | json_query('[?tags.contains(@, `app`)]') }}"
        db_servers: "{{ cmdb_servers.json | json_query('[?tags.contains(@, `db`)]') }}"

    # Create address objects from CMDB data
    - name: Create address objects from CMDB
      cdot65.scm.address:
        name: "{{ item.hostname }}"
        folder: "CMDB-Managed"
        description: "CMDB managed server: {{ item.hostname }}"
        ip_netmask: "{{ item.ip_address }}/32"
        tags:
          - "cmdb-managed"
          - "{{ item.environment }}"
      loop: "{{ web_servers + app_servers + db_servers }}"
      register: address_results

    # Create address groups
    - name: Create address groups
      cdot65.scm.address_group:
        name: "{{ item.key }}-servers"
        folder: "CMDB-Managed"
        description: "CMDB {{ item.key }} servers"
        static_members: "{{ item.value | map(attribute='hostname') | list }}"
      loop:
        - { key: "web", value: "{{ web_servers }}" }
        - { key: "app", value: "{{ app_servers }}" }
        - { key: "db", value: "{{ db_servers }}" }
      when: item.value | length > 0

    # Commit changes
    - name: Commit CMDB changes
      cdot65.scm.commit:
        description: "CMDB synchronization"

Performance Optimization

Parallel Task Execution

Optimize performance with parallel execution:

---
- name: Parallel SCM Configuration
  hosts: localhost
  connection: local

  vars:
    max_parallel: 10
    folders:
      - "Prod"
      - "Dev"
      - "Test"
      - "QA"

  tasks:
    # Async tasks for parallel processing
    - name: Create security zones in parallel
      cdot65.scm.security_zone:
        name: "Zone-{{ item }}"
        folder: "{{ item }}"
        description: "Zone for {{ item }}"
      loop: "{{ folders }}"
      async: 300
      poll: 0
      register: async_results

    # Wait for all async tasks to complete
    - name: Wait for async tasks
      async_status:
        jid: "{{ async_result_item.ansible_job_id }}"
      loop: "{{ async_results.results }}"
      loop_control:
        loop_var: async_result_item
      register: async_poll_results
      until: async_poll_results.finished
      retries: 30
      delay: 10

Bulk Operations

Optimize with bulk operations:

---
- name: Bulk Operations
  hosts: localhost
  connection: local

  vars:
    csv_file: "address_objects.csv"

  tasks:
    # Read CSV file
    - name: Read CSV file
      community.general.read_csv:
        path: "{{ csv_file }}"
      register: csv_data

    # Process in batches
    - name: Process in batches
      block:
        - name: Create address objects in batches
          cdot65.scm.bulk_address:
            folder: "SharedFolder"
            objects: "{{ batch_items }}"
          loop: "{{ csv_data.list | batch(20) | list }}"
          loop_control:
            loop_var: batch_items
      rescue:
        - name: Fall back to individual creation
          cdot65.scm.address:
            name: "{{ item.name }}"
            folder: "SharedFolder"
            ip_netmask: "{{ item.ip_netmask }}"
          loop: "{{ csv_data.list }}"

Advanced Debugging and Logging

Comprehensive Logging

Implement comprehensive logging:

---
- name: SCM with Advanced Logging
  hosts: localhost
  connection: local

  vars:
    log_file: "/var/log/ansible/scm_operations.log"
    debug_mode: true

  tasks:
    # Setup logging
    - name: Ensure log directory exists
      file:
        path: "/var/log/ansible"
        state: directory
        mode: '0755'
      become: true

    # Log the start of operations
    - name: Log operation start
      copy:
        content: |
          --------------------------------------
          SCM Operation: {{ ansible_date_time.iso8601 }}
          User: {{ lookup('env', 'USER') }}
          Playbook: {{ playbook_dir | basename }}/{{ ansible_play_name }}
          --------------------------------------
        dest: "{{ log_file }}"
        mode: '0644'
        append: yes
      become: true

    # Perform operations with debug
    - name: Create address with debug
      cdot65.scm.address:
        name: "test-server"
        folder: "SharedFolder"
        ip_netmask: "10.1.1.1/32"
        debug: "{{ debug_mode }}"
      register: address_result

    # Log the operation results
    - name: Log operation result
      copy:
        content: |
          Operation: Create address object
          Status: {{ 'Success' if address_result.changed else 'No change' }}
          Object ID: {{ address_result.scm_object.id | default('N/A') }}
          --------------------------------------
        dest: "{{ log_file }}"
        mode: '0644'
        append: yes
      become: true

Integration with Cloud Providers

Multi-Cloud Security Management

Manage security across multiple clouds:

---
- name: Multi-Cloud Security Management
  hosts: localhost
  connection: local

  vars:
    aws_regions:
      - us-east-1
      - us-west-2
    azure_resource_groups:
      - "production-rg"
      - "development-rg"

  tasks:
    # Get AWS security groups
    - name: Get AWS security groups
      amazon.aws.ec2_group_info:
        region: "{{ item }}"
      loop: "{{ aws_regions }}"
      register: aws_security_groups

    # Get Azure NSGs
    - name: Get Azure NSGs
      azure.azcollection.azure_rm_securitygroup_info:
        resource_group: "{{ item }}"
      loop: "{{ azure_resource_groups }}"
      register: azure_nsgs

    # Create address objects for all cloud resources
    - name: Create AWS VPC address objects
      cdot65.scm.address:
        name: "AWS-VPC-{{ item.vpc_id }}"
        folder: "Cloud-Resources"
        description: "AWS VPC {{ item.vpc_id }} CIDR"
        ip_netmask: "{{ item.cidr_block }}"
        tags:
          - "aws"
          - "vpc"
      loop: "{{ aws_vpcs.vpcs }}"
      when: aws_vpcs is defined

    # Create security rules based on cloud security groups
    - name: Create SCM rules from AWS security groups
      cdot65.scm.security_rule:
        name: "AWS-SG-{{ item.group_id | replace('sg-', '') }}"
        folder: "Cloud-Resources"
        description: "AWS Security Group {{ item.group_name }}"
        source_zones: ["AWS-Cloud"]
        destination_zones: ["AWS-Cloud"]
        source_addresses: ["any"]
        destination_addresses: ["AWS-VPC-{{ item.vpc_id }}"]
        applications: "{{ sg_apps[item.group_id] | default(['any']) }}"
        services: "{{ sg_services[item.group_id] | default(['application-default']) }}"
        action: "allow"
      loop: "{{ aws_security_groups | json_query('results[*].security_groups[*]') | flatten }}"
      vars:
        sg_apps: {}  # Mapping of SG to applications
        sg_services: {}  # Mapping of SG to services

These advanced examples demonstrate the depth and flexibility of the SCM Ansible Collection for complex enterprise environments and automation workflows.