Some checks are pending
Bidi Control Character Guard / bidi-control-guard (push) Waiting to run
Circular Dependency Check / Check for new circular dependencies (push) Waiting to run
Citus Migration Smoke / Combined migrations on single-node Citus (push) Waiting to run
E2E Fresh Install Tests / fresh-install-e2e (push) Waiting to run
ext-v2 guardrails / Run ext-v2 guard and ESLint (push) Waiting to run
Integration Tests / Check for relevant changes (push) Waiting to run
Integration Tests / ${{ (github.event_name == 'schedule' || github.event.inputs.suite == 'full') && 'Full integration suite' || 'Tier-1 integration subset' }} (push) Blocked by required conditions
Mobile checks / Mobile lint + typecheck (push) Waiting to run
Mobile checks / Mobile unit tests (push) Waiting to run
Mobile checks / Mobile dependency audit (report) (push) Waiting to run
Mobile checks / Mobile reproducibility checks (push) Waiting to run
Secrets guard (env backups) / Ensure no tracked env backup files (push) Waiting to run
Temporal Readiness / fast-readiness (push) Waiting to run
Temporal Readiness / docker-parity (push) Waiting to run
TypeScript Type Check / Nx affected typecheck (push) Waiting to run
Unit Tests / Skipped-test budget (push) Waiting to run
Unit Tests / Nx affected unit tests (push) Waiting to run
Unit Tests / Server unit coverage (informational) (push) Waiting to run
Validate Tenant Management Schema / Check for relevant changes (push) Waiting to run
Validate Tenant Management Schema / Validate Tenant Management Schema (push) Blocked by required conditions
EE Workflows Build Guard / ee-workflows-build-guard (push) Waiting to run
Excluded: .git, node_modules, secrets/, compose.env, assemblyscript tgz Source: /opt/alga-psa on psa.joliet.tech
521 lines
21 KiB
JavaScript
521 lines
21 KiB
JavaScript
/**
|
|
* Seed the "Active Directory to Microsoft 365 Migration" project template for all tenants
|
|
*
|
|
* NOTE: estimated_hours is stored in MINUTES (not hours) in the database
|
|
*/
|
|
const crypto = require('crypto');
|
|
|
|
// Use Node.js built-in crypto.randomUUID() instead of uuid package
|
|
const uuidv4 = () => crypto.randomUUID();
|
|
|
|
const TEMPLATE_NAME = 'Active Directory to Microsoft 365 Migration';
|
|
const TEMPLATE_CATEGORY = 'Migration';
|
|
const TEMPLATE_DESCRIPTION = 'Standard template for migrating from on-premises Active Directory to Azure AD/Entra ID with Microsoft 365. Covers assessment, environment preparation, migration execution, and validation.';
|
|
|
|
// Standard status definitions with colors
|
|
const STANDARD_STATUSES = [
|
|
{ name: 'To Do', color: '#6B7280', is_closed: false },
|
|
{ name: 'In Progress', color: '#3B82F6', is_closed: false },
|
|
{ name: 'Blocked', color: '#EF4444', is_closed: false },
|
|
{ name: 'Done', color: '#10B981', is_closed: true }
|
|
];
|
|
|
|
/**
|
|
* Convert hours to minutes for database storage
|
|
*/
|
|
const hoursToMinutes = (hours) => Math.round(hours * 60);
|
|
|
|
/**
|
|
* Build the template data structure
|
|
* Using fractional indexing for order_key (a0, a1, a2, etc.)
|
|
* NOTE: estimated_hours values are in MINUTES
|
|
* @param {string} tenant - Tenant ID
|
|
* @param {Map<string, string>} statusMappingsByName - Map of status name to mapping ID
|
|
*/
|
|
function buildTemplateData(tenant, statusMappingsByName) {
|
|
const templateId = uuidv4();
|
|
|
|
// Phase IDs
|
|
const phase1Id = uuidv4();
|
|
const phase2Id = uuidv4();
|
|
const phase3Id = uuidv4();
|
|
const phase4Id = uuidv4();
|
|
|
|
// Task IDs (named for checklist references)
|
|
const task1_1_Id = uuidv4(); // Audit AD structure
|
|
const task1_2_Id = uuidv4(); // Document group policies
|
|
const task1_3_Id = uuidv4(); // Inventory applications
|
|
const task1_4_Id = uuidv4(); // Define migration approach
|
|
const task1_5_Id = uuidv4(); // Create communication plan
|
|
const task2_1_Id = uuidv4(); // Configure M365 tenant
|
|
const task2_2_Id = uuidv4(); // Set up Azure AD Connect
|
|
const task2_3_Id = uuidv4(); // Configure SSO
|
|
const task2_4_Id = uuidv4(); // Set up Conditional Access
|
|
const task2_5_Id = uuidv4(); // Test pilot user sync
|
|
const task3_1_Id = uuidv4(); // Migrate pilot group
|
|
const task3_2_Id = uuidv4(); // Execute phased migration
|
|
const task3_3_Id = uuidv4(); // Migrate security groups
|
|
const task3_4_Id = uuidv4(); // Reconfigure app auth
|
|
const task3_5_Id = uuidv4(); // Update DNS
|
|
const task4_1_Id = uuidv4(); // Verify authentication
|
|
const task4_2_Id = uuidv4(); // Test app access
|
|
const task4_3_Id = uuidv4(); // User training
|
|
const task4_4_Id = uuidv4(); // Document environment
|
|
const task4_5_Id = uuidv4(); // Decommission AD
|
|
|
|
// Get status mapping for "To Do" by name (not by index)
|
|
const toDoStatusMappingId = statusMappingsByName.get('To Do');
|
|
|
|
const phases = [
|
|
{
|
|
tenant,
|
|
template_phase_id: phase1Id,
|
|
template_id: templateId,
|
|
phase_name: 'Assessment & Planning',
|
|
description: 'Audit current AD environment and plan the migration approach',
|
|
duration_days: 5,
|
|
start_offset_days: 0,
|
|
order_key: 'a0'
|
|
},
|
|
{
|
|
tenant,
|
|
template_phase_id: phase2Id,
|
|
template_id: templateId,
|
|
phase_name: 'Environment Preparation',
|
|
description: 'Configure M365 tenant and set up synchronization',
|
|
duration_days: 5,
|
|
start_offset_days: 5,
|
|
order_key: 'a1'
|
|
},
|
|
{
|
|
tenant,
|
|
template_phase_id: phase3Id,
|
|
template_id: templateId,
|
|
phase_name: 'Migration & Cutover',
|
|
description: 'Execute phased user migration and reconfigure applications',
|
|
duration_days: 7,
|
|
start_offset_days: 10,
|
|
order_key: 'a2'
|
|
},
|
|
{
|
|
tenant,
|
|
template_phase_id: phase4Id,
|
|
template_id: templateId,
|
|
phase_name: 'Validation & Cleanup',
|
|
description: 'Verify migration success and decommission old infrastructure',
|
|
duration_days: 5,
|
|
start_offset_days: 17,
|
|
order_key: 'a3'
|
|
}
|
|
];
|
|
|
|
const tasks = [
|
|
// Phase 1: Assessment & Planning
|
|
{
|
|
tenant,
|
|
template_task_id: task1_1_Id,
|
|
template_phase_id: phase1Id,
|
|
task_name: 'Audit current AD structure (users, groups, OUs)',
|
|
description: 'Document all users, security groups, distribution lists, and organizational units in the current Active Directory environment.',
|
|
estimated_hours: hoursToMinutes(3),
|
|
duration_days: 1,
|
|
template_status_mapping_id: toDoStatusMappingId,
|
|
order_key: 'a0'
|
|
},
|
|
{
|
|
tenant,
|
|
template_task_id: task1_2_Id,
|
|
template_phase_id: phase1Id,
|
|
task_name: 'Document group policies and permissions',
|
|
description: 'Review and document all GPOs, NTFS permissions, and security configurations that may need to be replicated or adjusted.',
|
|
estimated_hours: hoursToMinutes(3),
|
|
duration_days: 1,
|
|
template_status_mapping_id: toDoStatusMappingId,
|
|
order_key: 'a1'
|
|
},
|
|
{
|
|
tenant,
|
|
template_task_id: task1_3_Id,
|
|
template_phase_id: phase1Id,
|
|
task_name: 'Inventory applications with AD dependencies',
|
|
description: 'Identify all applications that authenticate against AD or rely on AD attributes for authorization.',
|
|
estimated_hours: hoursToMinutes(2),
|
|
duration_days: 1,
|
|
template_status_mapping_id: toDoStatusMappingId,
|
|
order_key: 'a2'
|
|
},
|
|
{
|
|
tenant,
|
|
template_task_id: task1_4_Id,
|
|
template_phase_id: phase1Id,
|
|
task_name: 'Define migration approach (hybrid vs cloud-only)',
|
|
description: 'Determine whether to use Azure AD Connect for hybrid identity or migrate to cloud-only authentication.',
|
|
estimated_hours: hoursToMinutes(2),
|
|
duration_days: 1,
|
|
template_status_mapping_id: toDoStatusMappingId,
|
|
order_key: 'a3'
|
|
},
|
|
{
|
|
tenant,
|
|
template_task_id: task1_5_Id,
|
|
template_phase_id: phase1Id,
|
|
task_name: 'Create user communication plan',
|
|
description: 'Develop communications to inform users about the migration timeline, what to expect, and any actions they need to take.',
|
|
estimated_hours: hoursToMinutes(1),
|
|
duration_days: 1,
|
|
template_status_mapping_id: toDoStatusMappingId,
|
|
order_key: 'a4'
|
|
},
|
|
|
|
// Phase 2: Environment Preparation
|
|
{
|
|
tenant,
|
|
template_task_id: task2_1_Id,
|
|
template_phase_id: phase2Id,
|
|
task_name: 'Configure M365 tenant and licenses',
|
|
description: 'Set up the Microsoft 365 tenant, verify domain ownership, and assign appropriate licenses.',
|
|
estimated_hours: hoursToMinutes(2),
|
|
duration_days: 1,
|
|
template_status_mapping_id: toDoStatusMappingId,
|
|
order_key: 'a0'
|
|
},
|
|
{
|
|
tenant,
|
|
template_task_id: task2_2_Id,
|
|
template_phase_id: phase2Id,
|
|
task_name: 'Set up Azure AD Connect (if hybrid)',
|
|
description: 'Install and configure Azure AD Connect for directory synchronization if using hybrid identity.',
|
|
estimated_hours: hoursToMinutes(4),
|
|
duration_days: 2,
|
|
template_status_mapping_id: toDoStatusMappingId,
|
|
order_key: 'a1'
|
|
},
|
|
{
|
|
tenant,
|
|
template_task_id: task2_3_Id,
|
|
template_phase_id: phase2Id,
|
|
task_name: 'Configure SSO and authentication methods',
|
|
description: 'Set up single sign-on, configure authentication methods (password hash sync, pass-through auth, or federation).',
|
|
estimated_hours: hoursToMinutes(3),
|
|
duration_days: 1,
|
|
template_status_mapping_id: toDoStatusMappingId,
|
|
order_key: 'a2'
|
|
},
|
|
{
|
|
tenant,
|
|
template_task_id: task2_4_Id,
|
|
template_phase_id: phase2Id,
|
|
task_name: 'Set up Conditional Access policies',
|
|
description: 'Configure Conditional Access policies for MFA, device compliance, and location-based access controls.',
|
|
estimated_hours: hoursToMinutes(3),
|
|
duration_days: 1,
|
|
template_status_mapping_id: toDoStatusMappingId,
|
|
order_key: 'a3'
|
|
},
|
|
{
|
|
tenant,
|
|
template_task_id: task2_5_Id,
|
|
template_phase_id: phase2Id,
|
|
task_name: 'Test pilot user sync',
|
|
description: 'Sync a pilot group of users and verify that attributes, group memberships, and authentication work correctly.',
|
|
estimated_hours: hoursToMinutes(2),
|
|
duration_days: 1,
|
|
template_status_mapping_id: toDoStatusMappingId,
|
|
order_key: 'a4'
|
|
},
|
|
|
|
// Phase 3: Migration & Cutover
|
|
{
|
|
tenant,
|
|
template_task_id: task3_1_Id,
|
|
template_phase_id: phase3Id,
|
|
task_name: 'Migrate pilot group and validate',
|
|
description: 'Complete migration for pilot users, have them test all applications and workflows, and gather feedback.',
|
|
estimated_hours: hoursToMinutes(3),
|
|
duration_days: 1,
|
|
template_status_mapping_id: toDoStatusMappingId,
|
|
order_key: 'a0'
|
|
},
|
|
{
|
|
tenant,
|
|
template_task_id: task3_2_Id,
|
|
template_phase_id: phase3Id,
|
|
task_name: 'Execute phased user migration',
|
|
description: 'Migrate remaining users in planned batches, monitoring for issues and adjusting as needed.',
|
|
estimated_hours: hoursToMinutes(6),
|
|
duration_days: 3,
|
|
template_status_mapping_id: toDoStatusMappingId,
|
|
order_key: 'a1'
|
|
},
|
|
{
|
|
tenant,
|
|
template_task_id: task3_3_Id,
|
|
template_phase_id: phase3Id,
|
|
task_name: 'Migrate security groups and permissions',
|
|
description: 'Ensure all security groups are synced and permissions are correctly applied in Azure AD.',
|
|
estimated_hours: hoursToMinutes(3),
|
|
duration_days: 1,
|
|
template_status_mapping_id: toDoStatusMappingId,
|
|
order_key: 'a2'
|
|
},
|
|
{
|
|
tenant,
|
|
template_task_id: task3_4_Id,
|
|
template_phase_id: phase3Id,
|
|
task_name: 'Reconfigure application authentication',
|
|
description: 'Update applications to authenticate against Azure AD instead of on-premises AD.',
|
|
estimated_hours: hoursToMinutes(4),
|
|
duration_days: 2,
|
|
template_status_mapping_id: toDoStatusMappingId,
|
|
order_key: 'a3'
|
|
},
|
|
{
|
|
tenant,
|
|
template_task_id: task3_5_Id,
|
|
template_phase_id: phase3Id,
|
|
task_name: 'Update DNS and domain federation',
|
|
description: 'Update DNS records and complete domain federation configuration for seamless SSO.',
|
|
estimated_hours: hoursToMinutes(2),
|
|
duration_days: 1,
|
|
template_status_mapping_id: toDoStatusMappingId,
|
|
order_key: 'a4'
|
|
},
|
|
|
|
// Phase 4: Validation & Cleanup
|
|
{
|
|
tenant,
|
|
template_task_id: task4_1_Id,
|
|
template_phase_id: phase4Id,
|
|
task_name: 'Verify all users can authenticate',
|
|
description: 'Confirm all migrated users can successfully sign in using their new credentials.',
|
|
estimated_hours: hoursToMinutes(2),
|
|
duration_days: 1,
|
|
template_status_mapping_id: toDoStatusMappingId,
|
|
order_key: 'a0'
|
|
},
|
|
{
|
|
tenant,
|
|
template_task_id: task4_2_Id,
|
|
template_phase_id: phase4Id,
|
|
task_name: 'Test application access',
|
|
description: 'Verify users can access all required applications with proper permissions.',
|
|
estimated_hours: hoursToMinutes(2),
|
|
duration_days: 1,
|
|
template_status_mapping_id: toDoStatusMappingId,
|
|
order_key: 'a1'
|
|
},
|
|
{
|
|
tenant,
|
|
template_task_id: task4_3_Id,
|
|
template_phase_id: phase4Id,
|
|
task_name: 'Conduct user training on new login process',
|
|
description: 'Train users on the new authentication experience, including MFA enrollment if applicable.',
|
|
estimated_hours: hoursToMinutes(2),
|
|
duration_days: 1,
|
|
template_status_mapping_id: toDoStatusMappingId,
|
|
order_key: 'a2'
|
|
},
|
|
{
|
|
tenant,
|
|
template_task_id: task4_4_Id,
|
|
template_phase_id: phase4Id,
|
|
task_name: 'Document new environment',
|
|
description: 'Update documentation to reflect the new Azure AD configuration, processes, and procedures.',
|
|
estimated_hours: hoursToMinutes(2),
|
|
duration_days: 1,
|
|
template_status_mapping_id: toDoStatusMappingId,
|
|
order_key: 'a3'
|
|
},
|
|
{
|
|
tenant,
|
|
template_task_id: task4_5_Id,
|
|
template_phase_id: phase4Id,
|
|
task_name: 'Decommission on-prem AD (if applicable)',
|
|
description: 'If moving to cloud-only, safely decommission on-premises domain controllers after confirming all services are migrated.',
|
|
estimated_hours: hoursToMinutes(3),
|
|
duration_days: 2,
|
|
template_status_mapping_id: toDoStatusMappingId,
|
|
order_key: 'a4'
|
|
}
|
|
];
|
|
|
|
// Checklist items for key tasks
|
|
const checklistItems = [
|
|
// Audit AD structure checklist
|
|
{ tenant, template_checklist_id: uuidv4(), template_task_id: task1_1_Id, item_name: 'Export user list from AD', order_number: 1 },
|
|
{ tenant, template_checklist_id: uuidv4(), template_task_id: task1_1_Id, item_name: 'Document security groups and memberships', order_number: 2 },
|
|
{ tenant, template_checklist_id: uuidv4(), template_task_id: task1_1_Id, item_name: 'Document distribution lists', order_number: 3 },
|
|
{ tenant, template_checklist_id: uuidv4(), template_task_id: task1_1_Id, item_name: 'Map OU structure', order_number: 4 },
|
|
{ tenant, template_checklist_id: uuidv4(), template_task_id: task1_1_Id, item_name: 'Identify service accounts', order_number: 5 },
|
|
|
|
// Configure M365 tenant checklist
|
|
{ tenant, template_checklist_id: uuidv4(), template_task_id: task2_1_Id, item_name: 'Verify domain ownership in M365 admin', order_number: 1 },
|
|
{ tenant, template_checklist_id: uuidv4(), template_task_id: task2_1_Id, item_name: 'Configure DNS records (MX, CNAME, TXT)', order_number: 2 },
|
|
{ tenant, template_checklist_id: uuidv4(), template_task_id: task2_1_Id, item_name: 'Assign licenses to pilot users', order_number: 3 },
|
|
{ tenant, template_checklist_id: uuidv4(), template_task_id: task2_1_Id, item_name: 'Configure tenant security defaults', order_number: 4 },
|
|
|
|
// Azure AD Connect checklist
|
|
{ tenant, template_checklist_id: uuidv4(), template_task_id: task2_2_Id, item_name: 'Verify server meets prerequisites', order_number: 1 },
|
|
{ tenant, template_checklist_id: uuidv4(), template_task_id: task2_2_Id, item_name: 'Install Azure AD Connect', order_number: 2 },
|
|
{ tenant, template_checklist_id: uuidv4(), template_task_id: task2_2_Id, item_name: 'Configure sync filtering (OUs/groups)', order_number: 3 },
|
|
{ tenant, template_checklist_id: uuidv4(), template_task_id: task2_2_Id, item_name: 'Choose authentication method', order_number: 4 },
|
|
{ tenant, template_checklist_id: uuidv4(), template_task_id: task2_2_Id, item_name: 'Run initial sync and verify', order_number: 5 },
|
|
|
|
// Conditional Access checklist
|
|
{ tenant, template_checklist_id: uuidv4(), template_task_id: task2_4_Id, item_name: 'Create MFA policy for all users', order_number: 1 },
|
|
{ tenant, template_checklist_id: uuidv4(), template_task_id: task2_4_Id, item_name: 'Configure trusted locations', order_number: 2 },
|
|
{ tenant, template_checklist_id: uuidv4(), template_task_id: task2_4_Id, item_name: 'Set up device compliance policy', order_number: 3 },
|
|
{ tenant, template_checklist_id: uuidv4(), template_task_id: task2_4_Id, item_name: 'Create break-glass admin account', order_number: 4 },
|
|
|
|
// Pilot validation checklist
|
|
{ tenant, template_checklist_id: uuidv4(), template_task_id: task3_1_Id, item_name: 'Test user sign-in from various devices', order_number: 1 },
|
|
{ tenant, template_checklist_id: uuidv4(), template_task_id: task3_1_Id, item_name: 'Verify MFA enrollment works', order_number: 2 },
|
|
{ tenant, template_checklist_id: uuidv4(), template_task_id: task3_1_Id, item_name: 'Test access to key applications', order_number: 3 },
|
|
{ tenant, template_checklist_id: uuidv4(), template_task_id: task3_1_Id, item_name: 'Collect pilot user feedback', order_number: 4 },
|
|
|
|
// Decommission AD checklist
|
|
{ tenant, template_checklist_id: uuidv4(), template_task_id: task4_5_Id, item_name: 'Verify no remaining AD dependencies', order_number: 1 },
|
|
{ tenant, template_checklist_id: uuidv4(), template_task_id: task4_5_Id, item_name: 'Backup AD before decommission', order_number: 2 },
|
|
{ tenant, template_checklist_id: uuidv4(), template_task_id: task4_5_Id, item_name: 'Demote domain controllers', order_number: 3 },
|
|
{ tenant, template_checklist_id: uuidv4(), template_task_id: task4_5_Id, item_name: 'Update documentation with new architecture', order_number: 4 }
|
|
];
|
|
|
|
return {
|
|
template: {
|
|
tenant,
|
|
template_id: templateId,
|
|
template_name: TEMPLATE_NAME,
|
|
description: TEMPLATE_DESCRIPTION,
|
|
category: TEMPLATE_CATEGORY,
|
|
created_by: null, // System-seeded template
|
|
use_count: 0
|
|
},
|
|
phases,
|
|
tasks,
|
|
checklistItems
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Find existing statuses or create if missing, then create status mappings for template
|
|
* Uses: To Do, In Progress, Blocked, Done
|
|
* @returns {Promise<{mappings: Array, mappingsByName: Map<string, string>}>}
|
|
*/
|
|
async function getOrCreateStandardStatusMappings(knex, tenant, templateId) {
|
|
const statusMappings = [];
|
|
const statusMappingsByName = new Map();
|
|
|
|
for (let i = 0; i < STANDARD_STATUSES.length; i++) {
|
|
const standardStatus = STANDARD_STATUSES[i];
|
|
|
|
// Look up existing status by name (case-insensitive)
|
|
let status = await knex('statuses')
|
|
.where({ tenant, status_type: 'project_task' })
|
|
.whereRaw('LOWER(name) = LOWER(?)', [standardStatus.name])
|
|
.first();
|
|
|
|
// Create only if not found (fallback for missing statuses like "Blocked")
|
|
if (!status) {
|
|
const maxOrder = await knex('statuses')
|
|
.where({ tenant, status_type: 'project_task' })
|
|
.max('order_number as max')
|
|
.first();
|
|
|
|
const newStatusId = uuidv4();
|
|
await knex('statuses').insert({
|
|
tenant,
|
|
status_id: newStatusId,
|
|
name: standardStatus.name,
|
|
status_type: 'project_task',
|
|
is_closed: standardStatus.is_closed,
|
|
order_number: (maxOrder?.max || 0) + 1,
|
|
color: standardStatus.color,
|
|
created_by: null
|
|
});
|
|
status = { status_id: newStatusId, color: standardStatus.color };
|
|
console.log(` Created missing status "${standardStatus.name}" for tenant`);
|
|
}
|
|
|
|
// Create status mapping for template
|
|
const mappingId = uuidv4();
|
|
statusMappings.push({
|
|
tenant,
|
|
template_status_mapping_id: mappingId,
|
|
template_id: templateId,
|
|
status_id: status.status_id,
|
|
custom_status_name: null,
|
|
custom_status_color: standardStatus.color,
|
|
display_order: i + 1
|
|
});
|
|
statusMappingsByName.set(standardStatus.name, mappingId);
|
|
}
|
|
|
|
return { mappings: statusMappings, mappingsByName: statusMappingsByName };
|
|
}
|
|
|
|
exports.up = async function(knex) {
|
|
console.log('Seeding AD to M365 Migration project template for all tenants...');
|
|
|
|
const tenants = await knex('tenants').select('tenant');
|
|
|
|
for (const { tenant } of tenants) {
|
|
// Check if this template already exists for the tenant
|
|
const existing = await knex('project_templates')
|
|
.where({
|
|
tenant,
|
|
template_name: TEMPLATE_NAME
|
|
})
|
|
.first();
|
|
|
|
if (existing) {
|
|
console.log(` Template already exists for tenant ${tenant}, skipping`);
|
|
continue;
|
|
}
|
|
|
|
const templateId = uuidv4();
|
|
|
|
// Get or create standard status mappings (To Do, In Progress, Blocked, Done)
|
|
const { mappings: statusMappings, mappingsByName: statusMappingsByName } =
|
|
await getOrCreateStandardStatusMappings(knex, tenant, templateId);
|
|
|
|
// Build template data with status mapping reference
|
|
const data = buildTemplateData(tenant, statusMappingsByName);
|
|
|
|
// Override the template_id to match
|
|
data.template.template_id = templateId;
|
|
data.phases.forEach(p => p.template_id = templateId);
|
|
|
|
// Insert in correct order: template first, then status mappings, then phases, then tasks, then checklists
|
|
await knex('project_templates').insert(data.template);
|
|
await knex('project_template_status_mappings').insert(statusMappings);
|
|
await knex('project_template_phases').insert(data.phases);
|
|
await knex('project_template_tasks').insert(data.tasks);
|
|
if (data.checklistItems && data.checklistItems.length > 0) {
|
|
await knex('project_template_checklist_items').insert(data.checklistItems);
|
|
}
|
|
|
|
console.log(` Created template for tenant ${tenant}`);
|
|
}
|
|
|
|
console.log('AD to M365 Migration template seeding complete');
|
|
};
|
|
|
|
exports.down = async function(knex) {
|
|
console.log('Removing AD to M365 Migration project template from all tenants...');
|
|
|
|
// Get all tenants and delete per-tenant to ensure proper Citus routing
|
|
const tenants = await knex('tenants').select('tenant');
|
|
|
|
for (const { tenant } of tenants) {
|
|
// Delete will cascade to phases, tasks, status mappings due to foreign keys
|
|
await knex('project_templates')
|
|
.where({
|
|
tenant,
|
|
template_name: TEMPLATE_NAME
|
|
})
|
|
.del();
|
|
}
|
|
|
|
console.log('AD to M365 Migration template removed');
|
|
};
|