Migration Reference
This page provides the technical reference for what each migration script migrates out-of-the-box, and how to extend them via configuration.
For the procedural steps that wrap these scripts, see Migration Process.
Project Online
Exporting data
The export script (ExportAllProjects.ps1) does not need to be modified, but its -ProjectFilter accepts OData expressions to limit the export.
OData operators
| Operator | Syntax |
|---|---|
| Equals | eq |
| Not equals | ne |
| Greater than | gt |
| Greater than or equal to | ge |
| Less than | lt |
| Less than or equal to | le |
Examples
| Filter value | Syntax |
|---|---|
| Text | EnterpriseProjectTypeName eq 'Major Project' |
| Numbers | ProjectFixedCost gt 0 |
| Guids | EnterpriseProjectTypeId eq guid'7473ef8c-9f25-e911-afb0-00155da06b17' |
| Booleans | ProjectEnterpriseFeatures eq true |
| Date | ProjectStartDate gt datetime'2024-09-01T00:00:00Z' |
The Project Online OData feed can be browsed at:
https://<tenantname>.sharepoint.com/sites/pwa/_api/ProjectData
Resources
OOTB script
| .MPP Table | Altus Table |
|---|---|
| Resource Entry | sensei_bookableresource |
| .MPP Field | Altus Field | Details | Example |
|---|---|---|---|
| Name | sensei_name |
First name, surname | Lucius Fox |
| User Logon Account | sensei_user |
First name, surname | Lucius Fox |
| Max. Units | sensei_targetutilization |
Default value set | 100 |
| Type | sensei_resourcetype |
Default value set | Named |
| Role | sensei_primaryrole |
Default value set | Unclassified |
| Calendar GUID | sensei_enterprisecalendar |
Default value set | Default Calendar |
Extending ImportResources.ps1
The script can be extended to migrate the following field types:
| Project Online type | Dataverse type | Sub-type |
|---|---|---|
| Cost | Currency | – |
| Date (date only) | Date and time | Date only |
| Date (date and time) | Date and time | Date and time |
| Duration | Whole number | Duration |
| Flag | Choice | Yes/no |
| Number (whole) | Whole number | – |
| Number (decimal) | Decimal | – |
| Text (single line) | Single line of text | Text |
| Lookup (single select) | Lookup | Text |
| Lookup (multi select) | Lookup (many-to-many) | Text |
Only the following parts of each block need to be updated:
| Attribute | Details |
|---|---|
| Project Online field | Display name as it appears in Project Online. |
| Altus field | Logical name of the Altus field. |
Default.ps1 reference |
Alias defined in Default.ps1 for lookup tables and choices. |
| Additional Altus variables | Relationship name, related table collection name, related table primary key (multi-select only). |
Code blocks
# Cost
$costValue = Get-ResourceCustomFieldTextValue -ProjectResource $ProjectResource `
-CustomFieldName 'Resource Example Cost Field'
if ($costValue) { $resourceBody['ce_resourceexamplecostfield'] = ($costValue -as [decimal]) }
# Date (date only and date/time both use this pattern)
$dateValue = Get-ReportingCustomFieldTextValue -ProjectResource $ProjectResource `
-CustomFieldName 'Resource Example Date Field'
if ($dateValue) {
$resourceBody['ce_resourceexampledatefield'] = ($dateValue -as [datetime]).ToString("yyyy-MM-dd")
}
# Duration
$durationValue = Get-ResourceCustomFieldTextValue -ProjectResource $ProjectResource `
-CustomFieldName 'Resource Example Duration Field'
if ($durationValue) {
$resourceBody['ce_resourceexampledurationfield'] = [int][double]$durationValue
}
# Flag → Flag
$cfValue = Get-ResourceCustomFieldTextValue -ProjectResource $ProjectResource `
-CustomFieldName 'Resource Example Flag Field'
if ($cfValue) {
$ynValue = ($cfValue -eq 'true') # must be $true / $false, not 1 / 0
$resourceBody['ce_resourceexampleflagfield'] = $ynValue
}
# Lookup → Flag
$cfValue = Get-ResourceCustomFieldTextValue -ProjectResource $ProjectResource `
-CustomFieldName 'Resource Example Choice Field'
if ($cfValue) {
$ynValue = ($cfValue -eq 'Yes')
$resourceBody['ce_resourceexampleflagfield2'] = $ynValue
}
# Number (whole or decimal)
$numberValue = Get-ResourceCustomFieldTextValue -ProjectResource $ProjectResource `
-CustomFieldName 'Resource Example Number Field'
if ($numberValue) { $resourceBody['ce_resourceexamplenumberfield'] = [int][double]$numberValue }
# Text (single line)
$textValue = Get-ResourceCustomFieldTextValue -ProjectResource $ProjectResource `
-CustomFieldName 'Resource Example text field'
if ($textValue) { $resourceBody['ce_resourceexampletextfield'] = [string]$textValue }
# Lookup → Choice
Set-ResourceChoiceField -ResourceBody $resourceBody -ProjectResource $ProjectResource `
-POLCustomFieldName 'Resource Example Choice Field' `
-ChoiceName 'choice_YesNo' `
-DataverseFieldName 'ce_resourceexamplechoicefield'
# Lookup (single select)
Set-ResourceLookupField -ResourceBody $resourceBody -ProjectResource $ProjectResource `
-POLCustomFieldName 'Resource Example Lookup Field' `
-LookupName 'lookup_EmploymentStatus' `
-DataverseFieldName 'ce_ResourceExampleLookupField'
# Lookup (multi select / many-to-many)
Set-ResourceManyToManyRelationship -ResourceBody $resourceBody -ProjectResource $ProjectResource `
-POLCustomFieldName 'Resource Example Multiselect Lookup Field' `
-LookupName 'lookup_MultiValueTable' `
-RelationshipName 'ce_sensei_bookableresource_ce_ExampleMultiSelectLookup_ce_ExampleMultiSelectLookup' `
-RelatedTableCollectionName 'ce_examplemultiselectlookups' `
-RelatedTablePrimaryKey 'ce_examplemultiselectlookupid'
Note
The -DataverseFieldName argument is case-sensitive for the lookup helpers.
Projects
OOTB script
| .MPP Table | Altus Table |
|---|---|
| Task Entry (Task Level 0) | sensei_project |
| .MPP Field | Altus Field | Details | Example |
|---|---|---|---|
| Task Name | sensei_name |
Where TaskID = 0 | Adelaide Fitness Project |
| N/A | sensei_projecttype |
Default value set | Major Project |
| N/A | sensei_externalprojectid |
Set the GUID = Project Online GUID | ProjectDesktop_d14bae12-39b3-e911-b07b-00155d9c3f18 |
Extending ImportProjects.ps1
The supported field types are identical to those listed for resources. The block patterns are also identical, but use the project helpers:
| Resource helper | Project helper |
|---|---|
Get-ResourceCustomFieldTextValue |
Get-ReportingCustomFieldTextValue |
Set-ResourceChoiceField |
Set-ProjectChoiceField |
Set-ResourceLookupField |
Set-ProjectLookupField |
Set-ResourceManyToManyRelationship |
Set-ProjectManyToManyRelationship |
$resourceBody / $ProjectResource |
$Project / $ReportingProject |
Example (single-select lookup):
Set-ProjectLookupField -ProjectBody $Project -ReportingProject $ReportingProject `
-POLCustomFieldName 'Example Lookup Field' `
-LookupName 'lookup_TeamName' `
-DataverseFieldName 'ce_ExampleLookupField'
Tasks
OOTB script
| .MPP Table | Altus Table |
|---|---|
| Task Entry | sensei_task |
| .MPP Field | Altus Field | Example |
|---|---|---|
| Task Name | Name | Identify Goals and Objectives |
| Resource Names | Assigned To | Project Manager |
| Start | Start | 2-12-19 |
| Finish | Finish | 6-12-19 |
| % Complete | Percent Complete | 25% |
| Work | Effort (Hours) | 40 |
| Remaining Work | Effort Remaining (Hours) | 30 |
| Actual Work | Effort Completed (Hours) | 10 |
| Duration | Duration (Days) | 5 days |
| Type | Task Type | Fixed Units |
| Task Mode | Manually Schedule | Yes |
| Notes | Notes | Goals were discussed |
| Deadline | Due Date | 6-12-19 |
| Summary | Summary | No |
| Milestone | Milestone | No |
| Locked | Locked | No |
Mapping of custom fields
There is no script block to update for tasks. The end-to-end process is:
- Create the task custom field in Altus.
- Choose an MS Project local field (e.g.
Date4) and map it to the Altus task custom field. - Run the
Set-CustomFieldMapping.ps1script to map the Project Online enterprise task custom field to the Project Desktop local field.
The local fields available in MS Project Desktop are limited (see Migration Process § 7 for the exact counts and the local fields reserved by Altus for Project).
Note
Cost and Duration custom task fields are not currently supported in the Altus task scheduler.
Lookup tables
LookupTablesToImport.ps1 is configured per source CSV with the following attributes:
| Attribute | Details |
|---|---|
SourceFile |
CSV exported from ExportLookupTableData.ps1. |
MapAsHierarchy |
$false for flat tables; $true for self-referential hierarchical tables. |
TableLogicalName |
Dataverse table logical (singular) name. |
TableCollectionName |
Dataverse table collection (plural) name. |
NameField |
Dataverse logical name of the Name field. |
DescriptionField |
(Optional) Dataverse logical name of the Description field. |
PrevLevelLookupField |
(Hierarchical only) Self-referential lookup field. |
Examples:
# Flat
@{
SourceFile = "LookupTable_Employment Status.csv"
MapAsHierarchy = $false
DataverseTable = @{
TableLogicalName = "ce_employmentstatus"
TableCollectionName = "ce_employmentstatuses"
NameField = "ce_name"
DescriptionField = "ce_description"
}
}
# Hierarchical
@{
SourceFile = "LookupTable_Skills.csv"
MapAsHierarchy = $true
DataverseTable = @{
TableLogicalName = "ce_skills"
TableCollectionName = "ce_skillses"
NameField = "ce_name"
DescriptionField = "ce_description"
PrevLevelLookupField = "ce_parent"
}
}
Cost rates
| MS Project | Altus |
|---|---|
| Resource Sheet | Bookable Resources |
| Cost Rate Table A | Bookable Resource > Rates |
| MS Project field | Altus display | Altus internal |
|---|---|---|
| Standard Rate | Rate (Hourly) | sensei_rate |
| Effective Date | Effective Date | sensei_effectivedate |
The publish script also migrates time-phased rates. There is no mechanism to extend this mapping.
Baselines
Fields below are stored on sensei_taskbaseline:
| MS Project field | Altus display | Altus internal |
|---|---|---|
| Baseline Start | Baseline Start | sensei_taskstart |
| Baseline Finish | Baseline Finish | sensei_taskfinish |
| Baseline Work | Baseline Effort | sensei_effort |
| Baseline Duration | Baseline Duration | sensei_duration |
All 11 Project Online baselines (0–5 protected, 6–10 unprotected) can be migrated by re-running Publish-MPPsToAltus.ps1 with the relevant -BaselineNumber value:
.\Publish-MPPsToAltus.ps1 -BaselineNumber 0 -IsReportable $true
.\Publish-MPPsToAltus.ps1 -BaselineNumber 1 -IsReportable $true
# ...
.\Publish-MPPsToAltus.ps1 -BaselineNumber 10 -IsReportable $true
-IsReportable $true flags the baseline as the reportable baseline in Altus.
SharePoint Online
Risks
OOTB script
| SharePoint List | Altus Table |
|---|---|
| Risks | sensei_risk |
| SharePoint field | Altus field | Example |
|---|---|---|
| Title | sensei_name |
Requirements Not Clearly Defined |
| Description | sensei_description |
Project requirements are incomplete or ambiguous… |
| DueDate | sensei_duedate |
30/04/2026 0:00 |
| Status | statuscode |
(1) Active |
| Category | sensei_category |
(2) Category2 |
| Probability | sensei_likelihood |
50% |
| Impact | sensei_consequence |
5 |
| Exposure | sensei_riskscore |
2.5 |
| Cost | sensei_cost |
$10,000.00 |
Mitigation_x0020_plan |
sensei_mitigationplan |
Conduct workshops with stakeholders… |
Contingency_x0020_plan |
sensei_contingencyplan |
Rebaseline scope and timeline… |
Trigger_x0020_Description |
sensei_triggerdescription |
When the due date is lapsed… |
| Trigger | sensei_trigger |
Date |
| Owner | sensei_riskowner |
John Price |
| AssignedTo | sensei_assignedto |
Marc Soester |
Modified script (export.config.json)
Each custom field is described by a JSON block. Only the highlighted attributes need to change:
| Attribute | Details |
|---|---|
spFieldInternalName |
SharePoint internal name. |
entityAttribute |
Logical name of the Altus column. |
type |
SharePoint type (see below). |
choiceMap (Choice only) |
SharePoint label → Altus option set value (e.g. 955000000). |
lookupEntity (Lookup only) |
Logical name of the related Altus table. |
Code blocks
// Text
{ "_fielddetails": "Single line of Text",
"spFieldInternalName": "ExampleTextField",
"entityAttribute": "ce_examplesptextfield",
"type": "Text" }
// Date & Time
{ "_fielddetails": "Date and Time",
"spFieldInternalName": "ExampleDateAndTimeField",
"entityAttribute": "ce_examplespdateandtimefield",
"type": "DateTime" }
// Date Only
{ "_fielddetails": "Date Only",
"spFieldInternalName": "ExampleDateOnlyField",
"entityAttribute": "ce_examplespdateonlyfield",
"type": "DateOnly" }
// Decimal
{ "_fielddetails": "Decimal",
"spFieldInternalName": "ExampleDecimalField",
"entityAttribute": "ce_examplespdecimalfield",
"type": "Decimal",
"divider": 2 }
// Whole number
{ "_fielddetails": "Whole Number",
"spFieldInternalName": "ExampleNumberField",
"entityAttribute": "ce_examplespnumberfield",
"type": "Number" }
// Lookup
{ "_fielddetails": "Lookup",
"spFieldInternalName": "ExampleLookupField",
"entityAttribute": "ce_examplesplookupdecisioncategory",
"type": "Lookup",
"lookupEntity": "DecisionCategory" }
// Money
{ "_fielddetails": "Money",
"spFieldInternalName": "ExampleMoneyField",
"entityAttribute": "ce_examplespmoneyfield",
"type": "Money" }
// Choice (OptionSet)
{ "_fielddetails": "OptionSet",
"spFieldInternalName": "ExampleOptionSetField",
"entityAttribute": "ce_examplespoptionsetfield",
"type": "OptionSet",
"choiceMap": {
"Resource": 955000000,
"Scope": 955000001,
"Schedule": 955000002,
"Technology": 955000003,
"Other": 955000004,
"Business Process": 955000005,
"Vendor": 955000006,
"Management Escalation": 955000007
}
}
// Yes/No (Boolean)
{ "_fielddetails": "Flag (Boolean)",
"spFieldInternalName": "ExampleBooleanField",
"entityAttribute": "ce_examplespbooleanfield",
"type": "Bool" }
Issues
OOTB script
| SharePoint List | Altus Table |
|---|---|
| Issues | sensei_issue |
| SharePoint field | Altus field | Example |
|---|---|---|
| Title | sensei_name |
Resource Overallocation |
| Discussion | sensei_description |
Key developer assigned to multiple projects causing delays. |
| DueDate | sensei_duedate |
24/04/2026 00:00 |
| Status | statuscode |
(1) Active |
| Category | sensei_category |
(3) Category3 |
| Priority | sensei_priority |
(2) Medium |
| AssignedTo | sensei_assignedto |
John Price |
Cost_x0020_Impact |
sensei_costimpact |
$10,000.00 |
| Resolution | sensei_resolution |
Rebalance workload and secure additional support. |
Modified script
Identical to Risks – Modified script. Reuse the same JSON blocks.
Custom lists
Each custom list entry in export.config.json requires:
| Attribute | Details |
|---|---|
spListTitle |
Title of the SharePoint list as shown in List Settings. |
entityLogicalName |
Logical name of the Altus table. |
projectLookupAttribute |
Lookup field on the Altus table that points to the project. |
projectLookupEntityLogicalName |
Logical name of the project entity (typically ce_sensei_project). |
projectLookupTextSource |
Source for the lookup text (e.g. Web.Title). |
backlinkAttribute |
Attribute that stores the source SharePoint URL. |
columnMap |
Array of field mappings (same structure as Risks/Issues). |
Example:
{
"spListTitle": "Actions",
"entityLogicalName": "ce_action",
"projectLookupAttribute": "ce_sensei_project",
"projectLookupEntityLogicalName": "ce_sensei_project",
"projectLookupTextSource": "Web.Title",
"backlinkAttribute": "sensei_sourceitemurl",
"columnMap": [
{ "spFieldInternalName": "Title", "entityAttribute": "ce_name", "type": "Text" },
{ "spFieldInternalName": "Description", "entityAttribute": "ce_description", "type": "Text" }
]
}
Documents
The document export script can be tuned with the following parameters:
| Parameter | Default | What it controls |
|---|---|---|
-ExcludeVersionHistory |
$true |
Set $false to include the full version history. |
-DetailedMetadata |
$false |
Set $true to export all document metadata. |
-ExcludeLibraries |
"Style Library,Preservation Hold Library,Form Templates,Recycle Bin,Site Assets" |
Comma-separated list of libraries to skip. Remove an entry to include that library. |
-ExcludeFilePatterns |
@("*.aspx") |
File-name patterns to skip. Add patterns to skip more types. |
-IncludeRootWeb |
(switch) | Include the root web in the export. |