Enterprise migrations from legacy Cisco Umbrella to Cisco Secure Access (combining Secure Internet Access, Cloud-Delivered Firewall, and Zero Trust Network Access) often stall when target configuration objects lack public REST API endpoints. While core DNS policies and IP suppression lists are exposed via the Umbrella API, complex objects such as custom application lists, do-not-decrypt inspection exclusions, and tenant-specific bypass rules often require manual configuration in the administrative console. When migrating thousands of object entries, manual entry is error-prone. This article details an automated pipeline using authenticated session harvesting, headless browser automation (Playwright/Selenium), and idempotent synchronization scripts.
Automating these administrative tasks eliminates configuration drift and reduces migration timelines from days of repetitive data entry to repeatable scripted executions.
Evaluating the API Gap: Public Endpoints vs Internal Console Operations
When auditing the management interfaces of cloud security consoles, configuration features frequently fall into two categories:
[Cisco Umbrella API (v2)] ────────► [Supported: Destination Lists, Users, Activity Reports]
│
▼ (Gap: Missing Endpoints)
[Admin Dashboard (SPA Console)] ──► [Unexposed: Application Exclusions, Custom SSL Bypass]
Public API endpoints provide robust CRUD access for standard network lists and DNS policies. However, newer or specialized management objects in single-page applications (SPAs) are often served by private internal GraphQL or REST microservices that are not documented for external integration.
When thousands of custom cloud application definitions and SSL inspection bypass rules must be transferred, engineering teams must either enter them manually via UI forms or bridge the automation gap programmatically.
Inspecting Private Dashboard Transactions
By inspecting network traffic within administrative browser sessions, we observe the exact payloads transmitted by the frontend console when creating configuration objects.
Key observations:
- Authentication: Administrative users authenticate via Cisco Duo SSO or SAML, receiving short-lived bearer tokens and session cookies.
- Private Endpoints: The management UI issues JSON POST or GraphQL requests directly to internal services, such as:
POST https://dashboard.umbrella.com/api/v2/organizations/{org_id}/custom-apps - Request Headers: Requests require explicit CSRF tokens (
X-CSRF-Token) and an authenticated session cookie bundle.
Headless Browser Automation Pipeline
When internal APIs cannot be easily invoked via standalone curl commands due to complex dynamic authentication challenges, a headless browser automation script bridges the gap reliably.
Extracting Legacy Configurations
First, export existing destination and application exclusion lists from the source Cisco Umbrella tenant using the standard v2 API:
#!/usr/bin/env python3
import requests
API_KEY = "UmbrellaApiKeyHere"
API_SECRET = "UmbrellaApiSecretHere"
ORG_ID = "1234567"
def fetch_destination_lists() -> list:
url = f"https://management.api.umbrella.com/v1/organizations/{ORG_ID}/destinationlists"
response = requests.get(url, auth=(API_KEY, API_SECRET), timeout=10)
response.raise_for_status()
return response.json()
if __name__ == "__main__":
dest_data = fetch_destination_lists()
print(f"Extracted {len(dest_data)} destination lists successfully.")
Automating Ingestion with Python and Selenium
For target console workflows lacking public APIs, use Selenium WebDriver with explicit wait conditions to populate target forms idempotently.
#!/usr/bin/env python3
import time
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
def create_headless_driver() -> webdriver.Chrome:
chrome_options = Options()
chrome_options.add_argument("--headless=new")
chrome_options.add_argument("--disable-gpu")
chrome_options.add_argument("--no-sandbox")
chrome_options.add_argument("--window-size=1920,1080")
return webdriver.Chrome(options=chrome_options)
def populate_bypass_list(driver: webdriver.Chrome, domains: list[str]) -> None:
# Explicit wait for target input element to be interactable
wait = WebDriverWait(driver, 15)
# Process domains in batches to avoid DOM buffer exhaustion
batch_size = 50
for i in range(0, len(domains), batch_size):
chunk = domains[i:i + batch_size]
text_payload = "\n".join(chunk)
input_box = wait.until(
EC.element_to_be_clickable((By.CSS_SELECTOR, "textarea[name='domain-entry-area']"))
)
input_box.clear()
input_box.send_keys(text_payload)
save_button = wait.until(
EC.element_to_be_clickable((By.XPATH, "//button[contains(text(), 'Add to List')]"))
)
save_button.click()
# Confirm successful toast notification before proceeding
wait.until(
EC.presence_of_element_located((By.CSS_SELECTOR, ".toast-success"))
)
print(f"Committed batch {i // batch_size + 1} ({len(chunk)} domains)")
if __name__ == "__main__":
driver = create_headless_driver()
try:
# Navigate to target console and perform authenticated session injection
driver.get("https://dashboard.umbrella.com")
# In a production pipeline, pre-authenticated session cookies are loaded here
finally:
driver.quit()
Validation and Post-Migration Audit
After executing automated configuration ingestion, verify policy parity between the source Umbrella organisation and the target Secure Access tenant:
- Object Count Parity: Validate that the count of imported domains matches the export manifest precisely.
- Synthetic Proxy Testing:
Test decryption exclusion behaviour through the Secure Web Gateway (SWG) using curl:
Inspect response headers to verify that the upstream proxy bypassed SSL inspection as configured.curl -vx secure-gateway.umbrella.com:80 -k -I https://internal-erp.corp.local - Policy Log Inspection: Review real-time activity search logs in the console to confirm that traffic matching the migrated application definitions triggers the correct security profiles.
Strategic Takeaways for Cloud Security Migrations
- Audit API coverage early: Identify gaps where target platforms lack programmatic provisioning before planning migration timeframes.
- Build idempotent scripts: Ensure automation scripts can be re-run safely without creating duplicate object entries or failing on existing records.
- Verify at the packet level: Use synthetic curl queries and certificate checks to validate that migrated security rules behave correctly in production.