diff --git a/docs/about/release-notes.md b/docs/about/release-notes.md
index 99cd82f..3927116 100644
--- a/docs/about/release-notes.md
+++ b/docs/about/release-notes.md
@@ -1,3 +1,8 @@
+## Version 0.3.4
+
+- Added support for External Dynamic Lists
+- Added support for Auto Tag Actions (not yet supported by API)
+
## Version 0.3.3
- Added support for URL Categories
diff --git a/docs/sdk/config/objects/external_dynamic_lists.md b/docs/sdk/config/objects/external_dynamic_lists.md
new file mode 100644
index 0000000..71266ef
--- /dev/null
+++ b/docs/sdk/config/objects/external_dynamic_lists.md
@@ -0,0 +1,374 @@
+# External Dynamic Lists Configuration Object
+
+## Table of Contents
+
+1. [Overview](#overview)
+2. [Core Methods](#core-methods)
+3. [EDL Model Attributes](#edl-model-attributes)
+4. [Exceptions](#exceptions)
+5. [Basic Configuration](#basic-configuration)
+6. [Usage Examples](#usage-examples)
+ - [Creating EDLs](#creating-edls)
+ - [Retrieving EDLs](#retrieving-edls)
+ - [Updating EDLs](#updating-edls)
+ - [Listing EDLs](#listing-edls)
+ - [Deleting EDLs](#deleting-edls)
+7. [Managing Configuration Changes](#managing-configuration-changes)
+ - [Performing Commits](#performing-commits)
+ - [Monitoring Jobs](#monitoring-jobs)
+8. [Error Handling](#error-handling)
+9. [Best Practices](#best-practices)
+10. [Full Script Examples](#full-script-examples)
+11. [Related Models](#related-models)
+
+## Overview
+
+The `ExternalDynamicLists` class provides functionality to manage External Dynamic Lists (EDLs) in Palo Alto Networks' Strata
+Cloud Manager. This class inherits from `BaseObject` and provides methods for creating, retrieving, updating, and deleting
+EDLs of various types including IP, Domain, URL, IMSI, and IMEI lists with configurable update intervals.
+
+## Core Methods
+
+| Method | Description | Parameters | Return Type |
+|------------|----------------------------|-----------------------------------------------|----------------------------------------|
+| `create()` | Creates a new EDL | `data: Dict[str, Any]` | `ExternalDynamicListsResponseModel` |
+| `get()` | Retrieves an EDL by ID | `edl_id: str` | `ExternalDynamicListsResponseModel` |
+| `update()` | Updates an existing EDL | `edl: ExternalDynamicListsUpdateModel` | `ExternalDynamicListsResponseModel` |
+| `delete()` | Deletes an EDL | `edl_id: str` | `None` |
+| `list()` | Lists EDLs with filtering | `folder: str`, `**filters` | `List[ExternalDynamicListsResponseModel]` |
+| `fetch()` | Gets EDL by name | `name: str`, `folder: str` | `ExternalDynamicListsResponseModel` |
+
+## EDL Model Attributes
+
+| Attribute | Type | Required | Description |
+|--------------------|--------------|--------------|---------------------------------------------|
+| `name` | str | Yes | Name of EDL (max 63 chars) |
+| `id` | UUID | Yes* | Unique identifier (*response only) |
+| `type` | TypeUnion | Yes | EDL type configuration |
+| `url` | str | Yes | Source URL for EDL content |
+| `description` | str | No | Description (max 255 chars) |
+| `exception_list` | List[str] | No | List of exceptions |
+| `auth` | AuthModel | No | Authentication credentials |
+| `recurring` | RecurringUnion| Yes | Update schedule configuration |
+| `folder` | str | Yes** | Folder location (**one container required) |
+| `snippet` | str | Yes** | Snippet location (**one container required) |
+| `device` | str | Yes** | Device location (**one container required) |
+
+## Exceptions
+
+| Exception | HTTP Code | Description |
+|------------------------------|-----------|--------------------------------|
+| `InvalidObjectError` | 400 | Invalid EDL data or format |
+| `MissingQueryParameterError` | 400 | Missing required parameters |
+| `NameNotUniqueError` | 409 | EDL name already exists |
+| `ObjectNotPresentError` | 404 | EDL not found |
+| `ReferenceNotZeroError` | 409 | EDL still referenced |
+| `AuthenticationError` | 401 | Authentication failed |
+| `ServerError` | 500 | Internal server error |
+
+## Basic Configuration
+
+
+
+
+```python
+from scm.client import Scm
+from scm.config.objects import ExternalDynamicLists
+
+# Initialize client
+client = Scm(
+ client_id="your_client_id",
+ client_secret="your_client_secret",
+ tsg_id="your_tsg_id"
+)
+
+# Initialize EDL object
+edls = ExternalDynamicLists(client)
+```
+
+
+
+## Usage Examples
+
+### Creating EDLs
+
+
+
+
+```python
+# IP-based EDL with daily updates
+ip_edl_config = {
+ "name": "malicious-ips",
+ "folder": "Texas",
+ "type": {
+ "ip": {
+ "url": "https://threatfeeds.example.com/ips.txt",
+ "description": "Known malicious IPs",
+ "recurring": {
+ "daily": {
+ "at": "03"
+ }
+ },
+ "auth": {
+ "username": "user123",
+ "password": "pass123"
+ }
+ }
+ }
+}
+
+# Create IP EDL
+ip_edl = edls.create(ip_edl_config)
+
+# Domain-based EDL with hourly updates
+domain_edl_config = {
+ "name": "blocked-domains",
+ "folder": "Texas",
+ "type": {
+ "domain": {
+ "url": "https://threatfeeds.example.com/domains.txt",
+ "description": "Blocked domains list",
+ "recurring": {
+ "hourly": {}
+ },
+ "expand_domain": True
+ }
+ }
+}
+
+# Create domain EDL
+domain_edl = edls.create(domain_edl_config)
+```
+
+
+
+### Retrieving EDLs
+
+
+
+
+```python
+# Fetch by name and folder
+edl = edls.fetch(name="malicious-ips", folder="Texas")
+print(f"Found EDL: {edl.name}")
+
+# Get by ID
+edl_by_id = edls.get(edl.id)
+print(f"Retrieved EDL: {edl_by_id.name}")
+```
+
+
+
+### Updating EDLs
+
+
+
+
+```python
+# Fetch existing EDL
+existing_edl = edls.fetch(name="malicious-ips", folder="Texas")
+
+# Update attributes
+existing_edl.description = "Updated malicious IP list"
+existing_edl.type.ip.recurring = {
+ "five_minute": {}
+}
+
+# Perform update
+updated_edl = edls.update(existing_edl)
+```
+
+
+
+### Listing EDLs
+
+
+
+
+```python
+# List with direct filter parameters
+filtered_edls = edls.list(
+ folder='Texas',
+ types=['ip', 'domain']
+)
+
+# Process results
+for edl in filtered_edls:
+ print(f"Name: {edl.name}")
+ if hasattr(edl.type, 'ip'):
+ print(f"Type: IP, URL: {edl.type.ip.url}")
+ elif hasattr(edl.type, 'domain'):
+ print(f"Type: Domain, URL: {edl.type.domain.url}")
+
+# Define filter parameters as dictionary
+list_params = {
+ "folder": "Texas",
+ "types": ["url"]
+}
+
+# List with filters as kwargs
+filtered_edls = edls.list(**list_params)
+```
+
+
+
+### Deleting EDLs
+
+
+
+
+```python
+# Delete by ID
+edl_id = "123e4567-e89b-12d3-a456-426655440000"
+edls.delete(edl_id)
+```
+
+
+
+## Managing Configuration Changes
+
+### Performing Commits
+
+
+
+
+```python
+# Prepare commit parameters
+commit_params = {
+ "folders": ["Texas"],
+ "description": "Updated EDL configurations",
+ "sync": True,
+ "timeout": 300 # 5 minute timeout
+}
+
+# Commit the changes
+result = edls.commit(**commit_params)
+
+print(f"Commit job ID: {result.job_id}")
+```
+
+
+
+### Monitoring Jobs
+
+
+
+
+```python
+# Get status of specific job
+job_status = edls.get_job_status(result.job_id)
+print(f"Job status: {job_status.data[0].status_str}")
+
+# List recent jobs
+recent_jobs = edls.list_jobs(limit=10)
+for job in recent_jobs.data:
+ print(f"Job {job.id}: {job.type_str} - {job.status_str}")
+```
+
+
+
+## Error Handling
+
+
+
+
+```python
+from scm.exceptions import (
+ InvalidObjectError,
+ MissingQueryParameterError,
+ NameNotUniqueError,
+ ObjectNotPresentError,
+ ReferenceNotZeroError
+)
+
+try:
+ # Create EDL configuration
+ edl_config = {
+ "name": "test-edl",
+ "folder": "Texas",
+ "type": {
+ "ip": {
+ "url": "https://example.com/ips.txt",
+ "description": "Test IP list",
+ "recurring": {
+ "daily": {
+ "at": "03"
+ }
+ }
+ }
+ }
+ }
+
+ # Create the EDL
+ new_edl = edls.create(edl_config)
+
+ # Commit changes
+ result = edls.commit(
+ folders=["Texas"],
+ description="Added test EDL",
+ sync=True
+ )
+
+ # Check job status
+ status = edls.get_job_status(result.job_id)
+
+except InvalidObjectError as e:
+ print(f"Invalid EDL data: {e.message}")
+except NameNotUniqueError as e:
+ print(f"EDL name already exists: {e.message}")
+except ObjectNotPresentError as e:
+ print(f"EDL not found: {e.message}")
+except ReferenceNotZeroError as e:
+ print(f"EDL still in use: {e.message}")
+except MissingQueryParameterError as e:
+ print(f"Missing parameter: {e.message}")
+```
+
+
+
+## Best Practices
+
+1. **EDL Configuration**
+ - Use descriptive names
+ - Set appropriate update intervals
+ - Configure authentication when needed
+ - Validate source URLs
+ - Monitor update status
+
+2. **Container Management**
+ - Always specify exactly one container
+ - Use consistent container names
+ - Validate container existence
+ - Group related EDLs
+
+3. **Update Scheduling**
+ - Choose appropriate intervals
+ - Consider source update frequency
+ - Stagger updates for multiple EDLs
+ - Monitor update success
+ - Handle failures gracefully
+
+4. **Performance**
+ - Use appropriate pagination
+ - Cache frequently accessed EDLs
+ - Monitor EDL sizes
+ - Consider update impact
+ - Implement retry logic
+
+5. **Security**
+ - Validate source URLs
+ - Use HTTPS where possible
+ - Secure credentials
+ - Monitor for malicious content
+ - Regular audits
+
+## Full Script Examples
+
+Refer to
+the [external_dynamic_lists.py example](https://github.com/cdot65/pan-scm-sdk/blob/main/examples/scm/config/objects/external_dynamic_lists.py).
+
+## Related Models
+
+- [ExternalDynamicListsCreateModel](../../models/objects/external_dynamic_lists_models.md#Overview)
+- [ExternalDynamicListsUpdateModel](../../models/objects/external_dynamic_lists_models.md#Overview)
+- [ExternalDynamicListsResponseModel](../../models/objects/external_dynamic_lists_models.md#Overview)
diff --git a/docs/sdk/config/objects/index.md b/docs/sdk/config/objects/index.md
index 2edb704..a7ed553 100644
--- a/docs/sdk/config/objects/index.md
+++ b/docs/sdk/config/objects/index.md
@@ -36,6 +36,10 @@ Manage application filters definitions, including their characteristics and asso
Manage application group definitions, including their characteristics and associated members.
+### [External Dynamic Lists](external_dynamic_lists.md)
+
+Manage EDLs.
+
### [Service](service.md)
Manage service definitions, including their characteristics and associated protocols / ports.
diff --git a/docs/sdk/index.md b/docs/sdk/index.md
index c155ee5..915f355 100644
--- a/docs/sdk/index.md
+++ b/docs/sdk/index.md
@@ -15,6 +15,7 @@ configuration objects and data models used to interact with Palo Alto Networks S
- [Application](config/objects/application.md)
- [Application Filters](config/objects/application_filters.md)
- [Application Group](config/objects/application_group.md)
+ - [External Dynamic Lists](config/objects/external_dynamic_lists.md)
- [Service](config/objects/service.md)
- [Service Group](config/objects/service_group.md)
- [Tag](config/objects/tag.md)
@@ -33,6 +34,7 @@ configuration objects and data models used to interact with Palo Alto Networks S
- [Application Models](models/objects/application_models.md)
- [Application Filters Models](models/objects/application_filters_models.md)
- [Application Group Models](models/objects/application_group_models.md)
+ - [External Dynamic Lists Models](models/objects/external_dynamic_lists_models.md)
- [Service Models](models/objects/service_models.md)
- [Service Group Models](models/objects/service_group_models.md)
- [Tag Models](models/objects/tag_models.md)
diff --git a/docs/sdk/models/objects/external_dynamic_lists_models.md b/docs/sdk/models/objects/external_dynamic_lists_models.md
new file mode 100644
index 0000000..81a8f9c
--- /dev/null
+++ b/docs/sdk/models/objects/external_dynamic_lists_models.md
@@ -0,0 +1,235 @@
+# External Dynamic Lists Models
+
+## Overview
+
+The External Dynamic Lists models provide a structured way to manage external dynamic lists in Palo Alto Networks' Strata
+Cloud Manager. These models support various types of dynamic lists including IP, domain, URL, IMSI, and IMEI lists, with
+configurable update intervals and authentication options.
+
+## Attributes
+
+| Attribute | Type | Required | Default | Description |
+|---------------------|---------------|----------|------------|-----------------------------------------------------------------------|
+| name | str | Yes | None | Name of the list. Max length: 63 chars. Must match pattern: ^[ a-zA-Z\d.\-_]+$ |
+| type | TypeUnion | Yes* | None | Type of dynamic list (predefined_ip, predefined_url, ip, domain, url, imsi, imei) |
+| folder | str | No** | None | Folder where list is defined. Max length: 64 chars |
+| snippet | str | No** | None | Snippet where list is defined. Max length: 64 chars |
+| device | str | No** | None | Device where list is defined. Max length: 64 chars |
+| id | UUID | Yes*** | None | UUID of the list (response only) |
+| description | str | No | None | Description of the list. Max length: 255 chars |
+| url | str | Yes | "http://" | URL for fetching list content |
+| exception_list | List[str] | No | None | List of exceptions |
+| certificate_profile | str | No | None | Client certificate profile name |
+| auth | AuthModel | No | None | Username/password authentication |
+| recurring | RecurringUnion | Yes | None | Update interval configuration |
+| expand_domain | bool | No | False | Enable domain expansion (domain type only) |
+
+\* Required for non-predefined lists
+\** Exactly one container type (folder/snippet/device) must be provided for create operations
+\*** Required for response model when snippet is not "predefined"
+
+## Exceptions
+
+The External Dynamic Lists models can raise the following exceptions during validation:
+
+- **ValueError**: Raised in several scenarios:
+ - When no container type or multiple container types are specified for create operations
+ - When ID is missing for non-predefined response models
+ - When type is missing for non-predefined response models
+ - When invalid recurring interval configuration is provided
+ - When invalid URL format is provided
+ - When name pattern validation fails
+
+## Model Validators
+
+### Container Type Validation
+
+For create operations, exactly one container type must be specified:
+
+
+
+
+```python
+from scm.models.objects import ExternalDynamicListsCreateModel
+
+# This will raise a validation error
+try:
+ edl = ExternalDynamicListsCreateModel(
+ name="blocked-ips",
+ folder="Shared",
+ device="fw01", # Can't specify both folder and device
+ type={"ip": {
+ "url": "http://example.com/blocked.txt",
+ "recurring": {"hourly": {}}
+ }}
+ )
+except ValueError as e:
+ print(e) # "Exactly one of 'folder', 'snippet', or 'device' must be provided."
+```
+
+
+
+### Recurring Interval Validation
+
+The models support various recurring update intervals:
+
+
+
+
+```python
+# Five minute interval
+edl = ExternalDynamicListsCreateModel(
+ name="blocked-ips",
+ folder="Shared",
+ type={"ip": {
+ "url": "http://example.com/blocked.txt",
+ "recurring": {"five_minute": {}}
+ }}
+)
+
+# Daily at specific hour
+edl = ExternalDynamicListsCreateModel(
+ name="blocked-ips",
+ folder="Shared",
+ type={"ip": {
+ "url": "http://example.com/blocked.txt",
+ "recurring": {"daily": {"at": "23"}}
+ }}
+)
+
+# Weekly on specific day and time
+edl = ExternalDynamicListsCreateModel(
+ name="blocked-ips",
+ folder="Shared",
+ type={"ip": {
+ "url": "http://example.com/blocked.txt",
+ "recurring": {"weekly": {"day_of_week": "monday", "at": "12"}}
+ }}
+)
+```
+
+
+
+## Usage Examples
+
+### Creating an IP List
+
+
+
+
+```python
+from scm.config.objects import ExternalDynamicLists
+
+# Using dictionary
+ip_list = {
+ "name": "blocked-ips",
+ "folder": "Shared",
+ "type": {
+ "ip": {
+ "description": "Blocked IP addresses",
+ "url": "http://example.com/blocked.txt",
+ "auth": {
+ "username": "user1",
+ "password": "pass123"
+ },
+ "recurring": {"hourly": {}}
+ }
+ }
+}
+
+edl = ExternalDynamicLists(api_client)
+response = edl.create(ip_list)
+```
+
+
+
+### Creating a Domain List
+
+
+
+
+```python
+# Using model directly
+from scm.models.objects import (
+ ExternalDynamicListsCreateModel,
+ DomainType,
+ DomainModel,
+ AuthModel,
+ HourlyRecurringModel
+)
+
+domain_list = ExternalDynamicListsCreateModel(
+ name="blocked-domains",
+ folder="Shared",
+ type=DomainType(
+ domain=DomainModel(
+ description="Blocked domains",
+ url="http://example.com/domains.txt",
+ auth=AuthModel(
+ username="user1",
+ password="pass123"
+ ),
+ recurring=HourlyRecurringModel(hourly={}),
+ expand_domain=True
+ )
+ )
+)
+
+payload = domain_list.model_dump(exclude_unset=True)
+response = edl.create(payload)
+```
+
+
+
+### Updating a List
+
+
+
+
+```python
+# Using dictionary
+update_dict = {
+ "id": "123e4567-e89b-12d3-a456-426655440000",
+ "name": "blocked-ips-updated",
+ "type": {
+ "ip": {
+ "description": "Updated blocked IPs",
+ "url": "http://example.com/blocked-new.txt",
+ "recurring": {"daily": {"at": "12"}}
+ }
+ }
+}
+
+response = edl.update(update_dict)
+```
+
+
+
+## Best Practices
+
+1. **List Management**
+ - Use descriptive names for lists
+ - Document list purposes in descriptions
+ - Configure appropriate update intervals
+ - Monitor list update status
+ - Review exception lists regularly
+
+2. **Security**
+ - Use HTTPS URLs when possible
+ - Implement proper authentication
+ - Use client certificates when available
+ - Regularly rotate credentials
+ - Monitor list content changes
+
+3. **Performance**
+ - Choose appropriate update intervals
+ - Monitor bandwidth usage
+ - Use exception lists efficiently
+ - Consider list size impacts
+ - Monitor update job status
+
+## Related Models
+
+- [AuthModel](../../models/objects/external_dynamic_lists_models.md#Overview)
+- [RecurringModels](../../models/objects/external_dynamic_lists_models.md#Overview)
+- [TypeModels](../../models/objects/external_dynamic_lists_models.md#Overview)
\ No newline at end of file
diff --git a/docs/sdk/models/objects/index.md b/docs/sdk/models/objects/index.md
index 23f08ef..649b202 100644
--- a/docs/sdk/models/objects/index.md
+++ b/docs/sdk/models/objects/index.md
@@ -21,6 +21,7 @@ For each configuration object, there are corresponding request and response mode
- [Application Models](application_models.md)
- [Application Filters Models](application_filters_models.md)
- [Application Group Models](application_group_models.md)
+- [External Dynamic Lists](external_dynamic_lists_models.md)
- [Service Models](service_models.md)
- [Service Group Models](service_group_models.md)
- [Tag Models](tag_models.md)
diff --git a/mkdocs.yml b/mkdocs.yml
index a0ca7a1..eb59714 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -42,6 +42,7 @@ nav:
- Application: sdk/config/objects/application.md
- Application Filters: sdk/config/objects/application_filters.md
- Application Group: sdk/config/objects/application_group.md
+ - External Dynamic Lists: sdk/config/objects/external_dynamic_lists.md
- Service: sdk/config/objects/service.md
- Service Group: sdk/config/objects/service_group.md
- Tag: sdk/config/objects/tag.md
@@ -62,6 +63,7 @@ nav:
- Application Models: sdk/models/objects/application_models.md
- Application Filter Models: sdk/models/objects/application_filters_models.md
- Application Group Models: sdk/models/objects/application_group_models.md
+ - External Dynamic Lists Models: sdk/models/objects/external_dynamic_lists_models.md
- Service Models: sdk/models/objects/service_models.md
- Service Group Models: sdk/models/objects/service_group_models.md
- Tag Models: sdk/models/objects/tag_models.md
diff --git a/pyproject.toml b/pyproject.toml
index 6511714..f743d3e 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[tool.poetry]
name = "pan-scm-sdk"
-version = "0.3.3"
+version = "0.3.4"
description = "Python SDK for Palo Alto Networks Strata Cloud Manager."
authors = ["Calvin Remsburg "]
license = "Apache 2.0"