# RMM Multi-Site Integration Architecture ## Problem Statement The current integration with Datto RMM only captures devices from a single site per company, leading to incomplete asset visibility for organizations with multiple locations. Since the PSA (Autotask) is the authoritative source for customers/companies, but RMM can have multiple sites per company, we're missing critical infrastructure data. ## Current Implementation Limitations ### Single Site Matching Issue - **Current Behavior**: `getDevicesByCompanyName()` finds the first matching site name and returns only those devices - **Data Loss**: Multi-site companies (branch offices, multiple locations) have devices that aren't tracked - **No Mapping System**: Unlike Auvik (NMS) integration, there's no database table or UI to map multiple RMM sites to a single PSA company ## Recommended Solution Architecture ### Option 1: Fuzzy Name Matching (Quick Fix) **Description**: Modify the RMM client to find ALL sites that could belong to a company using pattern matching. **Implementation**: ```typescript async getDevicesByCompanyName(companyName: string): Promise { const sites = await this.getSites(); // Find all sites that might belong to this company const matchingSites = sites.filter(site => { const siteName = site.name.toLowerCase(); const company = companyName.toLowerCase(); return siteName.includes(company) || siteName.startsWith(company) || // Handle patterns like "Company - Location" siteName.split('-')[0].trim() === company; }); // Get devices from all matching sites const allDevices = await Promise.all( matchingSites.map(site => this.getDevicesBySite(site.uid)) ); return allDevices.flat(); } ``` **Pros**: - Quick to implement - Catches obvious naming patterns - No database changes required **Cons**: - Prone to false positives/negatives - Not maintainable long-term - No audit trail - Can't handle complex naming schemes ### Option 2: Site Mapping Table (RECOMMENDED) **Description**: Create a proper many-to-one mapping between RMM sites and PSA companies, similar to the Auvik tenant mapping system. **Database Schema**: ```sql CREATE TABLE rmm_site_mappings ( id SERIAL PRIMARY KEY, company_id INTEGER NOT NULL REFERENCES companies(id) ON DELETE CASCADE, rmm_site_uid VARCHAR(255) NOT NULL, rmm_site_name VARCHAR(255) NOT NULL, is_primary BOOLEAN DEFAULT false, notes TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, created_by VARCHAR(255), UNIQUE(company_id, rmm_site_uid) ); CREATE INDEX idx_rmm_site_mappings_company ON rmm_site_mappings(company_id); CREATE INDEX idx_rmm_site_mappings_site_uid ON rmm_site_mappings(rmm_site_uid); ``` **Key Features**: - Multiple sites per company - Primary site designation - Audit trail with timestamps - Notes field for documentation - Foreign key constraints for data integrity **Enhanced Service Method**: ```typescript async getDevicesByCompanyId(companyId: number): Promise { // Get all mapped site UIDs from database const siteMappings = await db.query( 'SELECT rmm_site_uid FROM rmm_site_mappings WHERE company_id = $1', [companyId] ); // Fetch devices from all mapped sites const allDevices = await Promise.all( siteMappings.map(mapping => this.getDevicesBySite(mapping.rmm_site_uid) ) ); return allDevices.flat(); } ``` ### Option 3: Smart Auto-Discovery (Advanced Enhancement) **Description**: Combine manual mapping with intelligent discovery and pattern recognition. **Features**: 1. **Pattern Recognition**: Analyze existing site names to detect patterns 2. **Company Hierarchy**: Support parent/child company relationships 3. **Auto-Suggestions**: When new sites appear, suggest likely company matches 4. **Custom Rules**: Allow regex or pattern-based rules for automatic assignment 5. **Machine Learning**: Use historical mapping decisions to improve suggestions **Auto-Discovery Algorithm**: ```typescript interface SiteMatchingSuggestion { siteUid: string; siteName: string; suggestedCompanyId: number; confidence: number; matchReason: string; } async function suggestSiteMappings(): Promise { const unmappedSites = await getUnmappedSites(); const companies = await getAllCompanies(); const suggestions: SiteMatchingSuggestion[] = []; for (const site of unmappedSites) { // Check exact name match const exactMatch = companies.find(c => c.name.toLowerCase() === site.name.toLowerCase() ); if (exactMatch) { suggestions.push({ siteUid: site.uid, siteName: site.name, suggestedCompanyId: exactMatch.id, confidence: 0.95, matchReason: 'Exact name match' }); continue; } // Check partial matches and patterns const partialMatches = companies.filter(c => { const companyName = c.name.toLowerCase(); const siteName = site.name.toLowerCase(); return ( siteName.includes(companyName) || companyName.includes(siteName) || levenshteinDistance(siteName, companyName) < 3 ); }); if (partialMatches.length === 1) { suggestions.push({ siteUid: site.uid, siteName: site.name, suggestedCompanyId: partialMatches[0].id, confidence: 0.75, matchReason: 'Partial name match' }); } } return suggestions; } ``` ## Implementation Phases ### Phase 1: Foundation (Week 1) - [x] Document current limitations and proposed solutions - [ ] Create database migration for `rmm_site_mappings` table - [ ] Build basic API endpoints for CRUD operations - [ ] Implement service layer for multi-site device fetching ### Phase 2: User Interface (Week 2) - [ ] Create RMM Site Mappings page (similar to Auvik Mappings) - [ ] Add search and filtering capabilities - [ ] Implement bulk mapping operations - [ ] Add export/import functionality for mappings ### Phase 3: Intelligence (Week 3) - [ ] Implement auto-discovery suggestions - [ ] Add pattern-based matching rules - [ ] Create notification system for new unmapped sites - [ ] Build reporting dashboard for mapping coverage ### Phase 4: Optimization (Week 4) - [ ] Add caching layer for site mappings - [ ] Implement bulk device sync for multiple sites - [ ] Create monitoring for site changes - [ ] Add API rate limiting and retry logic ## Additional Considerations ### Performance Optimization - **Caching**: Store site mappings in Redis/memory cache - **Batch Processing**: Fetch devices from multiple sites in parallel - **Pagination**: Handle large device counts with proper pagination - **Rate Limiting**: Respect RMM API limits when fetching from multiple sites ### Data Integrity - **Validation**: Ensure sites aren't mapped to multiple companies - **Cleanup**: Handle deleted sites and companies gracefully - **Sync Status**: Track last sync time per site - **Error Handling**: Implement retry logic for failed site syncs ### User Experience - **Bulk Operations**: Allow mapping multiple sites at once - **Import/Export**: Support CSV import for initial setup - **Search**: Implement fuzzy search for sites and companies - **Reporting**: Show coverage metrics and unmapped sites ### Security & Compliance - **Audit Trail**: Log all mapping changes with user identification - **Permissions**: Implement role-based access for mapping management - **Data Privacy**: Ensure site data doesn't leak between companies - **Backup**: Regular backup of mapping configurations ## API Endpoint Specifications ### GET /api/rmm/site-mappings Returns all site mappings with optional filtering Query Parameters: - `companyId`: Filter by specific company - `unmapped`: Show only unmapped sites - `search`: Search term for site or company names ### POST /api/rmm/site-mappings Create or update a site mapping Request Body: ```json { "companyId": 123, "rmmSiteUid": "site-uid-123", "rmmSiteName": "Company - Branch Office", "isPrimary": false, "notes": "Main branch location" } ``` ### DELETE /api/rmm/site-mappings/:id Remove a site mapping ### POST /api/rmm/site-mappings/suggestions Get auto-discovery suggestions for unmapped sites Response: ```json { "suggestions": [ { "siteUid": "site-123", "siteName": "Acme Corp - Dallas", "suggestedCompanyId": 456, "suggestedCompanyName": "Acme Corp", "confidence": 0.85, "matchReason": "Partial name match" } ] } ``` ### POST /api/rmm/site-mappings/bulk Create multiple mappings at once Request Body: ```json { "mappings": [ { "companyId": 123, "rmmSiteUid": "site-1", "rmmSiteName": "Site 1" }, { "companyId": 123, "rmmSiteUid": "site-2", "rmmSiteName": "Site 2" } ] } ``` ## Success Metrics - **Coverage Rate**: % of RMM sites mapped to companies - **Device Visibility**: Total devices visible after multi-site implementation - **Sync Performance**: Time to sync devices across all sites - **User Adoption**: % of companies with multi-site mappings configured - **Error Rate**: Failed sync attempts per site ## Migration Strategy 1. **Backup Current Data**: Export existing single-site mappings 2. **Run Migration**: Create new mapping table structure 3. **Import Existing**: Convert current implicit mappings to explicit ones 4. **Validate**: Ensure no data loss during migration 5. **Monitor**: Track sync performance and error rates ## Conclusion Implementing Option 2 (Site Mapping Table) with elements of Option 3 (Smart Auto-Discovery) provides the best balance of accuracy, maintainability, and user experience. This approach mirrors the successful Auvik integration pattern while addressing the unique challenges of multi-site RMM environments.