#!/usr/bin/env python3 """ Maintenance Flow Seed Script for ResolutionFlow. Creates sample maintenance flows (scheduled multi-target tasks) for common MSP infrastructure maintenance work. Run from the backend directory with: python -m scripts.seed_maintenance_flows --email admin@resolutionflow.example.com --password TestPass123! Requirements: - Backend server must be running (uvicorn app.main:app) """ import asyncio import argparse import httpx from typing import Any # API Configuration API_BASE_URL = "http://localhost:8000/api/v1" ADMIN_EMAIL = None ADMIN_PASSWORD = None # ============================================================================= # MAINTENANCE FLOW DEFINITIONS # ============================================================================= def get_sonicwall_firmware_flow() -> dict[str, Any]: """SonicWall Firewall Firmware Update — check and install latest firmware.""" return { "name": "SonicWall Firmware Update", "description": "Check for and install the latest firmware on SonicWall firewalls. Includes pre-flight backup, compatibility check, staged install, and post-update verification. Designed for batch execution across multiple client firewalls.", "tree_type": "maintenance", "tags": ["sonicwall", "firmware", "firewall", "maintenance", "security"], "intake_form": [ { "variable_name": "firewall_ip", "label": "Firewall Management IP", "field_type": "ip_address", "required": True, "placeholder": "e.g. 192.168.1.1", "help_text": "Management IP or FQDN of the SonicWall appliance", "group_name": "Firewall Details", "display_order": 1, }, { "variable_name": "firewall_model", "label": "SonicWall Model", "field_type": "select", "required": True, "options": [ "TZ270 / TZ270W", "TZ370 / TZ370W", "TZ470 / TZ470W", "TZ570 / TZ570W", "TZ670", "NSa 2700", "NSa 3700", "NSa 4700", "NSa 5700", "NSa 6700", "NSsp 10700 / 11700 / 13700", "NSv (Virtual)", "Other / Legacy", ], "group_name": "Firewall Details", "display_order": 2, }, { "variable_name": "current_firmware", "label": "Current Firmware Version", "field_type": "text", "required": False, "placeholder": "e.g. 7.0.1-5145", "help_text": "Check under System > Status in the SonicWall UI", "group_name": "Firewall Details", "display_order": 3, }, { "variable_name": "maintenance_window", "label": "Maintenance Window", "field_type": "text", "required": True, "placeholder": "e.g. Sat 10pm-2am EST", "help_text": "Approved maintenance window for this client", "group_name": "Scheduling", "display_order": 4, }, { "variable_name": "client_name", "label": "Client Name", "field_type": "text", "required": True, "placeholder": "e.g. Contoso Ltd", "group_name": "Scheduling", "display_order": 5, }, { "variable_name": "ticket_number", "label": "Ticket Number", "field_type": "text", "required": False, "placeholder": "e.g. TKT-2024-1234", "group_name": "Scheduling", "display_order": 6, }, ], "tree_structure": { "steps": [ { "id": "step_1", "type": "procedure_step", "title": "Pre-Flight: Verify Access & Current State", "content_type": "verification", "estimated_minutes": 5, "description": ( "Log into the SonicWall at **[VAR:firewall_ip]** for **[VAR:client_name]**.\n\n" "**Verify:**\n" "1. Navigate to **System > Status** — note current firmware version\n" "2. Confirm the appliance model matches **[VAR:firewall_model]**\n" "3. Check **System > Diagnostics > Tech Support Report** — note any existing issues\n" "4. Verify HA status if applicable (both units should be in sync)\n" "5. Check VPN tunnel status — note how many tunnels are active\n" "6. Confirm you are within the maintenance window: **[VAR:maintenance_window]**" ), "verification": { "type": "checkbox", "prompt": "Logged in, current firmware noted, and within maintenance window?" }, }, { "id": "step_2", "type": "procedure_step", "title": "Export Configuration Backup", "content_type": "action", "estimated_minutes": 3, "description": ( "**Create a full configuration backup before any changes.**\n\n" "1. Navigate to **System > Settings > Export Settings**\n" "2. Click **Export** to download the `.exp` configuration file\n" "3. Save the file as: `[VAR:client_name]_SonicWall_[VAR:firewall_model]_pre-firmware_YYYY-MM-DD.exp`\n" "4. Store the backup in the client's documentation folder and password vault\n\n" "**Also export:**\n" "- **System > Diagnostics > Tech Support Report** (for rollback reference)\n\n" "**Critical:** Do NOT proceed without a verified backup file." ), "verification": { "type": "checkbox", "prompt": "Configuration backup exported and stored securely?" }, }, { "id": "step_3", "type": "procedure_step", "title": "Check for Available Firmware", "content_type": "action", "estimated_minutes": 5, "description": ( "**Check MySonicWall for the latest firmware:**\n\n" "1. Navigate to **System > Settings > Firmware & Backups**\n" "2. If connected to MySonicWall, available firmware will show automatically\n" "3. Alternatively, log into [mysonicwall.com](https://mysonicwall.com) → **Product Management** → select this appliance → **Firmware**\n\n" "**Before downloading, check:**\n" "- Read the **Release Notes** for the new firmware version\n" "- Look for any known issues that affect **[VAR:firewall_model]**\n" "- Check if the upgrade path requires intermediate firmware versions\n" "- Verify the firmware is marked **General Release** (not Early Release unless approved)\n\n" "**Note the target firmware version in your session notes.**" ), "notes_enabled": True, "verification": { "type": "checkbox", "prompt": "Target firmware identified and release notes reviewed?" }, }, { "id": "step_4", "type": "procedure_step", "title": "Upload & Install Firmware", "content_type": "action", "estimated_minutes": 15, "warning_text": "The firewall will reboot during this step. All active connections will drop temporarily.", "description": ( "**Install the firmware:**\n\n" "1. Navigate to **System > Settings > Firmware & Backups**\n" "2. Upload the downloaded firmware file (or click **Download** if fetching from MySonicWall)\n" "3. Select **Upload Firmware (with current settings)**\n" "4. **Important:** Select **Create backup of current firmware** before proceeding\n" "5. Click **Boot** to begin the upgrade\n\n" "**The appliance will:**\n" "- Save the current firmware to the backup partition\n" "- Flash the new firmware\n" "- Reboot (takes 3-8 minutes depending on model)\n\n" "**For HA pairs:** Update the secondary unit first, verify, then failover and update the primary.\n\n" "**Wait for the appliance to come back online.** Monitor the management IP with continuous ping." ), "commands": [ {"language": "bash", "code": "ping -t [VAR:firewall_ip]", "label": "Monitor reboot (Windows)"}, {"language": "bash", "code": "ping [VAR:firewall_ip]", "label": "Monitor reboot (Mac/Linux)"}, ], "verification": { "type": "checkbox", "prompt": "Firmware installed and appliance is back online?" }, }, { "id": "step_5", "type": "procedure_step", "title": "Post-Update Verification", "content_type": "verification", "estimated_minutes": 10, "description": ( "**Verify the firmware update was successful:**\n\n" "1. Log into **[VAR:firewall_ip]** — confirm the login page loads\n" "2. Navigate to **System > Status** — confirm new firmware version\n" "3. Check **System > Diagnostics > Tech Support Report** for errors\n\n" "**Verify critical services:**\n" "- [ ] Internet connectivity passing through the firewall\n" "- [ ] VPN tunnels re-established (compare count from pre-flight)\n" "- [ ] NAT rules working (test external-facing services)\n" "- [ ] Content filtering / security services active\n" "- [ ] DHCP leases being issued (if SonicWall is DHCP server)\n" "- [ ] SSL VPN / Global VPN Client connectivity\n" "- [ ] HA sync status (if applicable)\n\n" "**If anything is broken:** Reboot to the backup firmware partition via **System > Settings > Firmware & Backups** → Boot backup firmware." ), "verification": { "type": "checkbox", "prompt": "All services verified and operational on new firmware?" }, }, { "id": "step_6", "type": "procedure_step", "title": "Document & Close", "content_type": "informational", "estimated_minutes": 5, "description": ( "**Update documentation:**\n\n" "1. Update the client's asset record with the new firmware version\n" "2. Note the upgrade date and any issues encountered\n" "3. Export a fresh **post-update configuration backup**\n" "4. Update the ticket **[VAR:ticket_number]** with results\n\n" "**Summary for [VAR:client_name]:**\n" "- Appliance: **[VAR:firewall_model]** at **[VAR:firewall_ip]**\n" "- Previous firmware: **[VAR:current_firmware]**\n" "- New firmware: *(note in session)*\n" "- Status: Operational" ), "verification": { "type": "checkbox", "prompt": "Documentation updated and post-upgrade backup stored?" }, }, { "id": "step_end", "type": "procedure_end", "title": "Firmware Update Complete", "description": ( "SonicWall firmware update complete for **[VAR:client_name]**.\n\n" "- Appliance: **[VAR:firewall_model]** at **[VAR:firewall_ip]**\n" "- All services verified operational\n" "- Configuration backup stored\n\n" "**Follow-up:** Monitor for 24-48 hours for any stability issues." ), }, ] }, } def get_windows_update_dc_flow() -> dict[str, Any]: """Windows Server 2022 Domain Controller — Windows Update maintenance.""" return { "name": "Windows Updates — Domain Controller (Server 2022)", "description": "Check for and install Windows updates on a Windows Server 2022 domain controller. Includes AD health checks, DFSR/SYSVOL verification, proper reboot sequencing, and post-update replication validation. Designed for batch execution across multiple DCs.", "tree_type": "maintenance", "tags": ["windows-server", "domain-controller", "windows-update", "active-directory", "maintenance"], "intake_form": [ { "variable_name": "server_name", "label": "Server Name", "field_type": "text", "required": True, "placeholder": "e.g. DC01", "help_text": "Hostname of the domain controller", "group_name": "Server Details", "display_order": 1, }, { "variable_name": "server_ip", "label": "Server IP Address", "field_type": "ip_address", "required": True, "placeholder": "e.g. 10.0.1.10", "group_name": "Server Details", "display_order": 2, }, { "variable_name": "domain_name", "label": "Domain Name", "field_type": "text", "required": True, "placeholder": "e.g. contoso.local", "group_name": "Server Details", "display_order": 3, }, { "variable_name": "is_fsmo_holder", "label": "FSMO Role Holder?", "field_type": "select", "required": True, "options": ["No — standard DC", "Yes — holds one or more FSMO roles", "Unknown — will check"], "help_text": "FSMO holders need extra care during patching", "group_name": "Server Details", "display_order": 4, }, { "variable_name": "maintenance_window", "label": "Maintenance Window", "field_type": "text", "required": True, "placeholder": "e.g. Sun 2am-6am EST", "group_name": "Scheduling", "display_order": 5, }, { "variable_name": "client_name", "label": "Client Name", "field_type": "text", "required": True, "placeholder": "e.g. Contoso Ltd", "group_name": "Scheduling", "display_order": 6, }, { "variable_name": "ticket_number", "label": "Ticket Number", "field_type": "text", "required": False, "placeholder": "e.g. TKT-2024-1234", "group_name": "Scheduling", "display_order": 7, }, ], "tree_structure": { "steps": [ { "id": "step_1", "type": "procedure_step", "title": "Pre-Flight: AD Health Check", "content_type": "verification", "estimated_minutes": 10, "description": ( "Connect to **[VAR:server_name]** (**[VAR:server_ip]**) for **[VAR:client_name]** via RDP or remote PowerShell.\n\n" "**Run the following health checks before patching:**" ), "commands": [ {"language": "powershell", "code": "# Check AD replication status\nrepadmin /replsummary\n\n# Check for replication failures\nrepadmin /showrepl", "label": "AD Replication Status"}, {"language": "powershell", "code": "# Run DC diagnostics\ndcdiag /v | Select-String -Pattern 'passed|failed'", "label": "DC Diagnostics"}, {"language": "powershell", "code": "# Check SYSVOL/NETLOGON shares\nnet share | Select-String 'SYSVOL|NETLOGON'\n\n# Check DFSR health\nGet-WinEvent -LogName 'DFS Replication' -MaxEvents 10 | Format-Table TimeCreated, Message -Wrap", "label": "SYSVOL & DFSR Check"}, {"language": "powershell", "code": "# Check FSMO roles on this DC\nnetdom query fsmo", "label": "FSMO Role Check"}, {"language": "powershell", "code": "# Check disk space (need at least 10GB free on C:)\nGet-PSDrive C | Select-Object @{N='FreeGB';E={[math]::Round($_.Free/1GB,2)}}", "label": "Disk Space Check"}, ], "verification": { "type": "checkbox", "prompt": "AD replication healthy, dcdiag passing, SYSVOL shared, sufficient disk space?" }, }, { "id": "step_2", "type": "procedure_step", "title": "Create System State Backup", "content_type": "action", "estimated_minutes": 15, "description": ( "**Create a system state backup before patching.**\n\n" "This captures Active Directory, SYSVOL, registry, and boot files — essential for DC recovery." ), "commands": [ {"language": "powershell", "code": "# Start system state backup (requires Windows Server Backup feature)\nwbadmin start systemstatebackup -backupTarget:C: -quiet", "label": "System State Backup"}, {"language": "powershell", "code": "# If Windows Server Backup not installed:\nInstall-WindowsFeature Windows-Server-Backup -IncludeManagementTools", "label": "Install Backup Feature (if needed)"}, ], "verification": { "type": "checkbox", "prompt": "System state backup completed successfully?" }, }, { "id": "step_3", "type": "procedure_step", "title": "Check for & Review Available Updates", "content_type": "action", "estimated_minutes": 10, "description": ( "**Scan for available updates and review before installing:**" ), "commands": [ {"language": "powershell", "code": "# Install PSWindowsUpdate module if not present\nif (-not (Get-Module -ListAvailable PSWindowsUpdate)) {\n Install-Module PSWindowsUpdate -Force -Confirm:$false\n}\nImport-Module PSWindowsUpdate\n\n# Check for available updates\nGet-WindowsUpdate -MicrosoftUpdate | Format-Table -AutoSize KB, Size, Title", "label": "List Available Updates"}, ], "notes_enabled": True, "verification": { "type": "checkbox", "prompt": "Available updates reviewed — no known bad patches in the list?" }, }, { "id": "step_4", "type": "procedure_step", "title": "Install Updates & Reboot", "content_type": "action", "estimated_minutes": 30, "warning_text": "The server will reboot. All users authenticating against this DC will failover to another DC. Ensure at least one other DC is available.", "description": ( "**Install all approved updates:**\n\n" "**Important for [VAR:domain_name]:**\n" "- If this is the **only DC**, schedule downtime — users will lose authentication during reboot\n" "- If FSMO holder: verify other DCs can service requests during reboot\n" "- Reboot will take 5-20 minutes depending on update count" ), "commands": [ {"language": "powershell", "code": "# Install all updates and auto-reboot\nInstall-WindowsUpdate -MicrosoftUpdate -AcceptAll -AutoReboot", "label": "Install Updates (auto-reboot)"}, {"language": "powershell", "code": "# Or install without auto-reboot (manual control)\nInstall-WindowsUpdate -MicrosoftUpdate -AcceptAll -IgnoreReboot\n\n# Then reboot manually when ready\nRestart-Computer -Force", "label": "Install Updates (manual reboot)"}, {"language": "bash", "code": "ping -t [VAR:server_ip]", "label": "Monitor reboot (Windows)"}, ], "verification": { "type": "checkbox", "prompt": "Updates installed and server is back online?" }, }, { "id": "step_5", "type": "procedure_step", "title": "Post-Update: Verify AD Health", "content_type": "verification", "estimated_minutes": 10, "description": ( "**After the server comes back online, verify everything is healthy:**" ), "commands": [ {"language": "powershell", "code": "# Verify replication is working\nrepadmin /replsummary\nrepadmin /showrepl", "label": "AD Replication Check"}, {"language": "powershell", "code": "# Run DC diagnostics again\ndcdiag /v | Select-String -Pattern 'passed|failed'", "label": "DC Diagnostics"}, {"language": "powershell", "code": "# Verify SYSVOL/NETLOGON shares\nnet share | Select-String 'SYSVOL|NETLOGON'\n\n# Verify DNS is resolving\nnslookup [VAR:domain_name] [VAR:server_ip]", "label": "Shares & DNS Check"}, {"language": "powershell", "code": "# Check for pending reboots\nGet-ItemProperty 'HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\WindowsUpdate\\Auto Update\\RebootRequired' -ErrorAction SilentlyContinue\nGet-ItemProperty 'HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Component Based Servicing\\RebootPending' -ErrorAction SilentlyContinue", "label": "Pending Reboot Check"}, {"language": "powershell", "code": "# Verify installed updates\nGet-HotFix | Sort-Object InstalledOn -Descending | Select-Object -First 10 | Format-Table HotFixID, InstalledOn, Description", "label": "Verify Installed Updates"}, ], "verification": { "type": "checkbox", "prompt": "AD replication healthy, dcdiag passing, DNS resolving, shares accessible?" }, }, { "id": "step_6", "type": "procedure_step", "title": "Document & Close", "content_type": "informational", "estimated_minutes": 5, "description": ( "**Update documentation:**\n\n" "1. Record which KB updates were installed\n" "2. Note the new OS build number: `winver` or `[System.Environment]::OSVersion`\n" "3. Update the client's patching log\n" "4. Update ticket **[VAR:ticket_number]**\n\n" "**Summary for [VAR:client_name]:**\n" "- Server: **[VAR:server_name]** (**[VAR:server_ip]**)\n" "- Domain: **[VAR:domain_name]**\n" "- FSMO: **[VAR:is_fsmo_holder]**\n" "- Status: Patched and operational" ), "verification": { "type": "checkbox", "prompt": "Patching log updated and ticket documented?" }, }, { "id": "step_end", "type": "procedure_end", "title": "Windows Updates Complete", "description": ( "Windows updates installed on **[VAR:server_name]** for **[VAR:client_name]**.\n\n" "- Domain: **[VAR:domain_name]**\n" "- AD replication: Healthy\n" "- All services verified operational\n\n" "**Follow-up:** Monitor event logs for 24 hours for any post-patch issues." ), }, ] }, } def get_ssl_cert_renewal_flow() -> dict[str, Any]: """SSL Certificate Renewal — renew and install SSL certs for client websites.""" return { "name": "SSL Certificate Renewal", "description": "Renew and install SSL/TLS certificates for client websites and web applications. Covers CSR generation, certificate purchase/renewal, installation, and validation. Designed for batch execution across multiple client domains.", "tree_type": "maintenance", "tags": ["ssl", "certificate", "tls", "website", "maintenance", "security"], "intake_form": [ { "variable_name": "domain_name", "label": "Domain Name", "field_type": "text", "required": True, "placeholder": "e.g. www.contoso.com", "help_text": "Primary domain the certificate covers", "group_name": "Certificate Details", "display_order": 1, }, { "variable_name": "cert_type", "label": "Certificate Type", "field_type": "select", "required": True, "options": [ "Single Domain (DV)", "Wildcard (*.domain.com)", "Multi-Domain / SAN", "Extended Validation (EV)", "Let's Encrypt (Free/Auto)", ], "group_name": "Certificate Details", "display_order": 2, }, { "variable_name": "hosting_type", "label": "Hosting / Server Type", "field_type": "select", "required": True, "options": [ "IIS (Windows Server)", "Apache (Linux)", "Nginx (Linux)", "cPanel / Plesk", "Azure App Service", "AWS (ELB / CloudFront)", "Cloudflare", "Other", ], "group_name": "Server Details", "display_order": 3, }, { "variable_name": "server_ip", "label": "Server IP / Hostname", "field_type": "text", "required": True, "placeholder": "e.g. 203.0.113.50 or web01.contoso.com", "help_text": "IP or hostname of the web server hosting this site", "group_name": "Server Details", "display_order": 4, }, { "variable_name": "cert_provider", "label": "Certificate Provider", "field_type": "select", "required": True, "options": [ "Let's Encrypt (Certbot)", "DigiCert", "Sectigo / Comodo", "GoDaddy", "GlobalSign", "Namecheap", "Other", ], "group_name": "Certificate Details", "display_order": 5, }, { "variable_name": "expiry_date", "label": "Current Cert Expiry Date", "field_type": "text", "required": False, "placeholder": "e.g. 2026-03-15", "help_text": "When the current certificate expires (YYYY-MM-DD)", "group_name": "Certificate Details", "display_order": 6, }, { "variable_name": "client_name", "label": "Client Name", "field_type": "text", "required": True, "placeholder": "e.g. Contoso Ltd", "group_name": "Project Info", "display_order": 7, }, { "variable_name": "ticket_number", "label": "Ticket Number", "field_type": "text", "required": False, "placeholder": "e.g. TKT-2024-1234", "group_name": "Project Info", "display_order": 8, }, ], "tree_structure": { "steps": [ { "id": "step_1", "type": "procedure_step", "title": "Pre-Flight: Check Current Certificate", "content_type": "verification", "estimated_minutes": 5, "description": ( "**Verify the current certificate status for [VAR:domain_name] ([VAR:client_name]):**\n\n" "Check the current certificate from the command line or browser:" ), "commands": [ {"language": "bash", "code": "# Check current certificate details and expiry\nopenssl s_client -connect [VAR:domain_name]:443 -servername [VAR:domain_name] 2>/dev/null | openssl x509 -noout -dates -subject -issuer", "label": "Check Current Cert (Linux/Mac)"}, {"language": "powershell", "code": "# Check current certificate from Windows\n$uri = 'https://[VAR:domain_name]'\n$req = [Net.HttpWebRequest]::Create($uri)\n$req.AllowAutoRedirect = $false\ntry { $req.GetResponse() | Out-Null } catch {}\n$cert = $req.ServicePoint.Certificate\nWrite-Host \"Subject: $($cert.Subject)\"\nWrite-Host \"Issuer: $($cert.Issuer)\"\nWrite-Host \"Expires: $($cert.GetExpirationDateString())\"", "label": "Check Current Cert (Windows)"}, {"language": "bash", "code": "# Quick SSL test via curl\ncurl -vI https://[VAR:domain_name] 2>&1 | grep -E 'expire|subject|issuer|SSL'", "label": "Quick SSL Check"}, ], "verification": { "type": "checkbox", "prompt": "Current certificate status verified — expiry date confirmed?" }, }, { "id": "step_2", "type": "procedure_step", "title": "Generate CSR (if required)", "content_type": "action", "estimated_minutes": 10, "description": ( "**Generate a Certificate Signing Request (CSR) if needed.**\n\n" "Skip this step if using **Let's Encrypt** (certbot handles CSR automatically) or if the provider generates it for you.\n\n" "Generate the CSR on the target server (**[VAR:server_ip]**):" ), "commands": [ {"language": "bash", "code": "# Generate private key and CSR (Linux/Apache/Nginx)\nopenssl req -new -newkey rsa:2048 -nodes \\\n -keyout [VAR:domain_name].key \\\n -out [VAR:domain_name].csr \\\n -subj \"/C=US/ST=State/L=City/O=[VAR:client_name]/CN=[VAR:domain_name]\"", "label": "Generate CSR (OpenSSL)"}, {"language": "powershell", "code": "# Generate CSR on IIS (Windows)\n# Open IIS Manager > Server Certificates > Create Certificate Request\n# Or via PowerShell:\n$params = @{\n Subject = 'CN=[VAR:domain_name],O=[VAR:client_name]'\n KeyLength = 2048\n HashAlgorithm = 'SHA256'\n CertStoreLocation = 'Cert:\\LocalMachine\\My'\n}\n$cert = New-SelfSignedCertificate @params\n# Then export CSR via certreq or IIS Manager", "label": "Generate CSR (IIS/Windows)"}, ], "verification": { "type": "checkbox", "prompt": "CSR generated (or skipped if using auto-renewal)?" }, }, { "id": "step_3", "type": "procedure_step", "title": "Purchase / Renew Certificate", "content_type": "action", "estimated_minutes": 15, "description": ( "**Renew or purchase the certificate from [VAR:cert_provider]:**\n\n" "**For Let's Encrypt:**" ), "commands": [ {"language": "bash", "code": "# Let's Encrypt renewal via Certbot\nsudo certbot renew --cert-name [VAR:domain_name]\n\n# Or for a new cert:\nsudo certbot certonly --webroot -w /var/www/html -d [VAR:domain_name]", "label": "Certbot Renewal"}, ], "notes_enabled": True, "verification": { "type": "checkbox", "prompt": "New certificate obtained — cert files downloaded/generated?" }, }, { "id": "step_4", "type": "procedure_step", "title": "Install Certificate on Server", "content_type": "action", "estimated_minutes": 10, "description": ( "**Install the new certificate on [VAR:hosting_type] at [VAR:server_ip]:**\n\n" "Follow the appropriate process for your hosting type. After installation, restart the web service to load the new certificate." ), "commands": [ {"language": "bash", "code": "# Apache — update ssl.conf and restart\nsudo cp [VAR:domain_name].crt /etc/ssl/certs/\nsudo cp [VAR:domain_name].key /etc/ssl/private/\nsudo systemctl restart apache2", "label": "Install on Apache"}, {"language": "bash", "code": "# Nginx — update server block and restart\nsudo cp [VAR:domain_name].crt /etc/nginx/ssl/\nsudo cp [VAR:domain_name].key /etc/nginx/ssl/\nsudo nginx -t && sudo systemctl restart nginx", "label": "Install on Nginx"}, {"language": "powershell", "code": "# IIS — Import certificate via PowerShell\n$cert = Import-PfxCertificate -FilePath 'C:\\certs\\[VAR:domain_name].pfx' -CertStoreLocation 'Cert:\\LocalMachine\\My' -Password (ConvertTo-SecureString -String 'YourPfxPassword' -AsPlainText -Force)\n\n# Then bind in IIS Manager:\n# Sites > [site] > Bindings > Edit HTTPS > Select new certificate", "label": "Install on IIS"}, ], "verification": { "type": "checkbox", "prompt": "Certificate installed and web service restarted?" }, }, { "id": "step_5", "type": "procedure_step", "title": "Validate New Certificate", "content_type": "verification", "estimated_minutes": 5, "description": ( "**Verify the new certificate is active and valid:**" ), "commands": [ {"language": "bash", "code": "# Verify the new cert is serving\nopenssl s_client -connect [VAR:domain_name]:443 -servername [VAR:domain_name] 2>/dev/null | openssl x509 -noout -dates -subject\n\n# Verify certificate chain is complete\nopenssl s_client -connect [VAR:domain_name]:443 -servername [VAR:domain_name] 2>/dev/null | grep -E 'Verify return|depth'", "label": "Verify New Cert"}, {"language": "bash", "code": "# Test HTTPS response\ncurl -I https://[VAR:domain_name]", "label": "Test HTTPS"}, ], "reference_url": "https://www.ssllabs.com/ssltest/", "verification": { "type": "checkbox", "prompt": "New certificate verified — correct domain, valid dates, complete chain?" }, }, { "id": "step_6", "type": "procedure_step", "title": "Document & Close", "content_type": "informational", "estimated_minutes": 3, "description": ( "**Update documentation:**\n\n" "1. Record the new certificate expiry date\n" "2. Update the client's certificate inventory\n" "3. Set a calendar reminder for 30 days before the new expiry\n" "4. Store any private keys securely in the password vault\n" "5. Update ticket **[VAR:ticket_number]**\n\n" "**Summary for [VAR:client_name]:**\n" "- Domain: **[VAR:domain_name]**\n" "- Type: **[VAR:cert_type]**\n" "- Provider: **[VAR:cert_provider]**\n" "- Server: **[VAR:hosting_type]** at **[VAR:server_ip]**\n" "- Previous expiry: **[VAR:expiry_date]**\n" "- New expiry: *(note in session)*\n" "- Status: Renewed and operational" ), "verification": { "type": "checkbox", "prompt": "Certificate inventory updated and renewal reminder set?" }, }, { "id": "step_end", "type": "procedure_end", "title": "SSL Certificate Renewed", "description": ( "SSL certificate renewed for **[VAR:domain_name]** (**[VAR:client_name]**).\n\n" "- Certificate type: **[VAR:cert_type]**\n" "- Provider: **[VAR:cert_provider]**\n" "- Hosting: **[VAR:hosting_type]**\n" "- All validations passed\n\n" "**Follow-up:** Verify no mixed-content warnings and test from multiple browsers." ), }, ] }, } # ============================================================================= # SEEDING FUNCTIONS # ============================================================================= async def get_admin_token(client: httpx.AsyncClient) -> str: """Authenticate and get admin token.""" response = await client.post( f"{API_BASE_URL}/auth/login", data={"username": ADMIN_EMAIL, "password": ADMIN_PASSWORD}, ) if response.status_code != 200: raise Exception(f"Login failed ({response.status_code}): {response.text}") return response.json()["access_token"] async def create_maintenance_flow(client: httpx.AsyncClient, token: str, flow_data: dict) -> dict | None: """Create a maintenance flow via the API. Returns None if it already exists.""" headers = {"Authorization": f"Bearer {token}"} flow_data["is_default"] = True flow_data["is_public"] = True flow_data["status"] = "published" # Check if flow with same name exists list_response = await client.get(f"{API_BASE_URL}/trees", headers=headers, params={"tree_type": "maintenance"}) if list_response.status_code == 200: existing = list_response.json() for tree in existing: if tree["name"] == flow_data["name"]: print(f" [SKIP] Flow '{flow_data['name']}' already exists (ID: {tree['id']})") return None response = await client.post( f"{API_BASE_URL}/trees", json=flow_data, headers=headers, ) if response.status_code not in (200, 201): raise Exception(f"Failed to create flow '{flow_data['name']}': {response.text}") tree = response.json() print(f" [OK] Created flow '{flow_data['name']}' (ID: {tree['id']})") return tree async def seed_maintenance_flows(): """Main seeding function.""" print("\n" + "=" * 60) print(" RESOLUTIONFLOW - Maintenance Flow Templates Seeder") print("=" * 60) async with httpx.AsyncClient(timeout=60.0) as client: try: health_check = await client.get(f"{API_BASE_URL.replace('/api/v1', '')}/health") if health_check.status_code != 200: print(f"\n[ERROR] API health check failed: {health_check.status_code}") return False except httpx.ConnectError: print("\n[ERROR] Cannot connect to API server") print(f" Make sure the server is running at {API_BASE_URL}") return False print("\n[1/3] Authenticating...") try: token = await get_admin_token(client) print(f" Logged in as {ADMIN_EMAIL}") except Exception as e: print(f" [ERROR] Failed to authenticate: {e}") return False print("\n[2/3] Preparing maintenance flows...") flows_to_create = [ get_sonicwall_firmware_flow(), get_windows_update_dc_flow(), get_ssl_cert_renewal_flow(), ] print(f" Found {len(flows_to_create)} flows to seed\n") print("[3/3] Creating maintenance flows...") created_count = 0 skipped_count = 0 for flow_data in flows_to_create: try: result = await create_maintenance_flow(client, token, flow_data) if result: created_count += 1 else: skipped_count += 1 except Exception as e: print(f" [FAIL] Failed to create '{flow_data['name']}': {e}") print("\n" + "=" * 60) print(" SEEDING COMPLETE") print("=" * 60) print(f" Flows created: {created_count}") print(f" Flows skipped (already exist): {skipped_count}") print() return True def main(): global ADMIN_EMAIL, ADMIN_PASSWORD, API_BASE_URL parser = argparse.ArgumentParser(description="Seed maintenance flow templates") parser.add_argument("--email", required=True, help="Admin email for authentication") parser.add_argument("--password", required=True, help="Admin password") parser.add_argument("--api-url", default=API_BASE_URL, help="API base URL") args = parser.parse_args() ADMIN_EMAIL = args.email ADMIN_PASSWORD = args.password API_BASE_URL = args.api_url asyncio.run(seed_maintenance_flows()) if __name__ == "__main__": main()