- Add admin dashboard with sync controls and data browser - Implement RMM, Auvik, and Addigy organization mappings - Add chunked ticket sync with progress tracking - Implement entity sync service with rate limiting - Add analytics engine and performance optimizer - Create data browser for all PSA entities - Add navigation components and UI improvements - Implement background processing and sync services - Add comprehensive documentation and migration scripts - Update configuration items with multi-system support - Enhance contact management and purchase history - Add issue type assignment and LLM analyzer - Improve error handling and logging utilities
9.7 KiB
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:
async getDevicesByCompanyName(companyName: string): Promise<DattoRMMDevice[]> {
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:
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:
async getDevicesByCompanyId(companyId: number): Promise<DattoRMMDevice[]> {
// 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:
- Pattern Recognition: Analyze existing site names to detect patterns
- Company Hierarchy: Support parent/child company relationships
- Auto-Suggestions: When new sites appear, suggest likely company matches
- Custom Rules: Allow regex or pattern-based rules for automatic assignment
- Machine Learning: Use historical mapping decisions to improve suggestions
Auto-Discovery Algorithm:
interface SiteMatchingSuggestion {
siteUid: string;
siteName: string;
suggestedCompanyId: number;
confidence: number;
matchReason: string;
}
async function suggestSiteMappings(): Promise<SiteMatchingSuggestion[]> {
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)
- Document current limitations and proposed solutions
- Create database migration for
rmm_site_mappingstable - 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 companyunmapped: Show only unmapped sitessearch: Search term for site or company names
POST /api/rmm/site-mappings
Create or update a site mapping
Request Body:
{
"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:
{
"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:
{
"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
- Backup Current Data: Export existing single-site mappings
- Run Migration: Create new mapping table structure
- Import Existing: Convert current implicit mappings to explicit ones
- Validate: Ensure no data loss during migration
- 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.