Migration Process
This page describes the end-to-end procedure to migrate Project Online and SharePoint Online data into Altus PPM.
Important
Complete every step in Prerequisites and Altus POL Migration SharePoint App before starting this process.
Process summary
Project Online
| Step | Script |
|---|---|
| 1. Export Project Online data | ExportAllProjects.ps1 |
| 2. Export lookup tables | ExportLookupTableData.ps1 |
| 3. Import lookup tables | ImportLookupTableData.ps1 (configured via LookupTablesToImport.ps1) |
| 4. Load lookup tables | Default.ps1 |
| 5. Map resource custom fields and create resources | ImportResources.ps1, AltusPOLMigration.ps1 |
| 6. Map project custom fields and create projects | ImportProjects.ps1, AltusPOLMigration.ps1 |
| 7. Map task custom fields | New-FieldMappingFile.ps1, Set-CustomFieldMapping.ps1 |
| 8. Run schedule migration | Publish-MPPsToAltus.ps1 |
| 9. Post-migration steps | (manual) |
SharePoint Online
| Step | Script |
|---|---|
Lists – Update export.config.json |
(config file) |
| Lists – Run export | 1-export-lists.ps1 |
| Lists – Run import | 2-import-lists.ps1 |
| Documents – Create/identify target library | (manual) |
| Documents – Run export | export script with required parameters |
| Documents – Post-migration | (manual) |
Project Online data migration
1. Export Project Online data
Download the scripts
- Open the Project Online Migration page on the Altus partner portal.
- Download
Altus.Project.Online.Migration.Toolkit.<date>.zip. - Unblock the zip file (if Windows reports it as blocked).
- Extract the scripts to a working folder, e.g.
C:\Users\<you>\Desktop\POLDataMigration.
Note
If you get errors after extraction, you may need to unblock individual .ps1 files via their Properties dialog.
Run the export
You will need:
| Property | Example |
|---|---|
| Project Online URL | https://<tenant>.sharepoint.com/sites/<pwa> |
| Script location | cd <path>\POLDataMigration\0.Project-Online-Extraction |
| Files location | <path>\POLDataMigration\2.Project-and-Resource-Migration\Files |
Important
Microsoft Project Desktop must be open during the export so it can save the .MPP files. Do not connect MS Project to Project Online; it just needs to be running.
Open SharePoint Online Management Shell.
Navigate to the export script folder.
Run the export, optionally filtering by project name (asterisk wildcard supported):
.\ExportAllProjects.ps1 ` -Url "https://<tenant>.sharepoint.com/sites/<pwa>" ` -OutputDirectory "<path>\2.Project-and-Resource-Migration\Files" ` -ProjectFilter @("Brisbane Roadmap Tool Implementation", "D365 Deployment")Sign in with the Project Online administrator account when prompted.
Note
The export script uses Microsoft's well-known ClientId d3590ed6-52b3-4102-aeff-aad2292ab01c; this does not need to be set. The Altus or custom ClientId is required by ExportLookupTableData.ps1 and Run-Migration.ps1 (see Altus POL Migration SharePoint App).
If you see Set-ExecutionPolicy errors, run:
Set-ExecutionPolicy -ExecutionPolicy Bypass -Scope Process -Force
Filtering with OData
The -ProjectFilter parameter accepts OData expressions. Operators:
| Operator | Syntax |
|---|---|
| Equals | eq |
| Not equals | ne |
| Greater than | gt |
| Greater than or equal | ge |
| Less than | lt |
| Less than or equal | le |
Examples:
| Filter | 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 API can be browsed at https://<tenant>.sharepoint.com/sites/pwa/_api/ProjectData.
2. Export lookup tables
Open SharePoint Online Management Shell.
Navigate to
1.Lookup-Table-Migration.Run:
.\ExportLookupTableData.ps1 ` -PwaUrl "https://<tenant>.sharepoint.com/sites/<pwa>" ` -ClientId "<your-client-id>"
The lookup tables are exported as .csv to 1.Lookup-Table-Migration\LookupTables.
3. Import lookup tables
Before running this step, the lookup tables must already exist in Altus (created in Prerequisites § Add configuration to a solution layer).
Configure the script
In your solution layer, open each lookup table and copy these values:
- Logical name (singular)
- Collection name (plural)
- Name field logical name
- (Hierarchical only) Parent field logical name
Open
LookupTablesToImport.ps1for editing.Update each entry with the source
.csvfile name and the table details. Example:# Flat lookup table @{ SourceFile = "LookupTable_Employment Status.csv" MapAsHierarchy = $false DataverseTable = @{ TableLogicalName = "ce_employmentstatus" TableCollectionName = "ce_employmentstatuses" NameField = "ce_name" DescriptionField = "ce_description" } } # Hierarchical lookup table @{ SourceFile = "LookupTable_Skills.csv" MapAsHierarchy = $true DataverseTable = @{ TableLogicalName = "ce_skills" TableCollectionName = "ce_skillses" NameField = "ce_name" DescriptionField = "ce_description" PrevLevelLookupField = "ce_parent" } }
Note
Logical names must be lowercase. Uncomment the script blocks before running (remove leading #).
Run the import
Open SharePoint Online Management Shell and navigate to
1.Lookup-Table-Migration.Run:
.\ImportLookupTableData.ps1Enter the Altus environment URL when prompted (e.g.
https://<env>.crm6.dynamics.com/).Sign in to Altus.
Errors are written to a log file in the same folder.
4. Load lookup tables
Update Default.ps1 so the project and resource import scripts can resolve lookup table values.
Open
2.Project-and-Resource-Migration\Default.ps1.Update
$LookupTablesToLoadwith each lookup table's table logical name, collection name, and name field. To get the Set Name for the collection, in Power Apps select the table > Tools > Copy Set Name.$LookupTablesToLoad = @( @{ Lookup = "lookup_RBS" DataverseTable = @{ TableLogicalName = "ce_rbs" TableCollectionName = "ce_rbses" NameField = "ce_name" } }, # ... )Update
$ChoiceFieldsToLoadfor each global/local choice field used by the import:$ChoiceFieldsToLoad = @( @{ Name = "choice_YesNo" IsGlobalChoice = $true DataverseTable = @{ TableLogicalName = "sensei_project" ChoiceField = "ce_examplechoicefield" } } )
Note
Uncomment the script blocks before running.
5. Map resource custom fields and create resources
Field type mapping
| 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 (via many-to-many) | Text |
Map resource custom fields
Edit ImportResources.ps1 and add a script block per custom field. Only the field name, Altus logical name, and any Default.ps1 alias references need to change. Refer to Migration Reference § Resources for the full set of code blocks.
Example mappings:
# Text
$textValue = Get-ResourceCustomFieldTextValue -ProjectResource $ProjectResource `
-CustomFieldName 'Resource Example text field'
if ($textValue) { $resourceBody['ce_resourceexampletextfield'] = [string]$textValue }
# Single-select lookup
Set-ResourceLookupField -ResourceBody $resourceBody -ProjectResource $ProjectResource `
-POLCustomFieldName 'Resource Example Lookup Field' `
-LookupName 'lookup_EmploymentStatus' `
-DataverseFieldName 'ce_ResourceExampleLookupField'
# Multi-select lookup (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
Dataverse field names in the lookup helpers (-DataverseFieldName) are case-sensitive.
For multi-select fields, the -RelationshipName, -RelatedTableCollectionName, and -RelatedTablePrimaryKey values are sourced from the table's Relationships and Properties panes in Power Apps.
Create resources
Open SharePoint Online Management Shell.
Navigate to
2.Project-and-Resource-Migration.Run:
.\AltusPOLMigration.ps1Enter the Altus environment URL.
Sign in.
Select the mode of operation:
- E – Execute (commit records)
- W – What-If (simulate)
Select 2: Import Resources (Named and Generic).
OOTB resource fields are mapped as documented in Migration Reference § Resources.
6. Map project custom fields and create projects
Map project custom fields
Edit ImportProjects.ps1. The pattern is the same as for resources but with the project helpers (Get-ReportingCustomFieldTextValue, Set-ProjectChoiceField, Set-ProjectLookupField, Set-ProjectManyToManyRelationship). See Migration Reference § Projects for the complete list of code blocks.
Create projects
- Open SharePoint Online Management Shell and navigate to
2.Project-and-Resource-Migration. - Run
.\AltusPOLMigration.ps1, sign in, select E or W, then select 3: Import Projects.
Important
Microsoft Project Desktop will open for each project as the script runs. Do not close it — closing MS Project will cause the script to fail. Resources must already have been imported (run the script twice if you also need to import resources).
A log file is generated when the script completes.
7. Map task custom fields
Project Online supports unlimited task-level enterprise custom fields, but MS Project Desktop is limited to a fixed set of local fields:
| Local field type | Number available |
|---|---|
| Date | 9 (Date1 – Date9) |
| Finish | 10 (Finish1 – Finish10) |
| Flag | 20 (Flag1 – Flag20) |
| Number | 20 (Number1 – Number20) |
| Start | 10 (Start1 – Start10) |
| Text | 26 (Text1 – Text26) |
| Outline Code | 10 (Outline Code1 – Outline Code10) |
The following local fields are reserved by Altus for Project:
| Scope | Local field | Reserved for |
|---|---|---|
| Task | Text 29 | Task GUID |
| Task | Text 30 | Task Hash |
| Task | Date 10 | Protected Actuals Before |
| Resource | Text 27 | Resource Role |
| Resource | Text 28 | Resource Type |
| Resource | Text 29 | Resource GUID |
| Resource | Text 30 | Resource Hash |
Note
Custom Cost and Duration task fields are not currently supported. Project Online task lookup fields are migrated to text fields in Project Desktop (no lookup picker). To preserve a controlled list, create the custom field in Altus only and edit it directly in the Altus schedule.
End-to-end process
- Create the task custom field in Altus (see Prerequisites § Create custom fields).
- Generate the
field-mappings.csvfile. - Set the field mapping in Altus.
- Run the script that maps Project Online enterprise task custom fields to the local MS Project Desktop fields.
Generate the .csv mapping file
Open MS Project Desktop.
Open SharePoint Online Management Shell (not as administrator).
Navigate to
3.Schedule-Migration.Run:
.\New-FieldMappingFile.ps1 ` -Path "<path>\2.Project-and-Resource-Migration\Files" ` -OutputPath ".\field-mappings.csv"When prompted, choose how decimal Project Online fields map to MS Project local fields.
Set the field mapping in Altus
- Open Altus > Settings > Altus for Project Configuration.
- Select New Field Mapping.
- Specify the Microsoft Project field and the Altus field as per
field-mappings.csv> OK. - Repeat for every task custom field.
Run the mapping script
.\Set-CustomFieldMapping.ps1 `
-MppFolder "<path>\2.Project-and-Resource-Migration\Files" `
-MappingFile "<path>\3.Schedule-Migration\field-mappings.csv"
8. Run schedule migration
Connect MS Project to Altus
- Open MS Project > Blank Project > save it.
- Select the Altus tab > Connect to Altus.
- Sign in and select the target Altus environment.
- When the project list loads, click Cancel.
Run the publish script
Open SharePoint Online Management Shell (not as administrator).
Navigate to
3.Schedule-Migration.Ensure MS Project is open and connected to Altus.
Run:
.\Publish-MPPsToAltus.ps1 ` -BaselineNumber 0 ` -IsReportable $true ` -Path "<path>\2.Project-and-Resource-Migration\Files"
Successfully published files are moved to ...\Files\Published. To re-run the migration, move them back to ...\Files.
Note
If your environments use different currencies, also pass -ProjectCurrency and set it to match Altus and Project Online (see Troubleshooting § Script Currency Issue).
To migrate additional baselines, change -BaselineNumber (0–10) and re-run the script. Use -IsReportable $true to flag a baseline as the reportable baseline in Altus.
Resource rates
The publish script also migrates resource rates from MS Project to Altus bookable resources. No additional action is required; matching resources are populated automatically.
| MS Project | Altus |
|---|---|
| Resource Sheet | Bookable Resources |
| Cost Rate Table A | Bookable Resource > Rates |
| Standard Rate | Rate (Hourly) – sensei_rate |
| Effective Date | Effective Date – sensei_effectivedate |
Time-phased resource rates are migrated for each time period.
9. Post-migration steps
The scripts populate only the basic project and resource information. Complete the following manually after the migration.
1. Set project type
Default for all projects is Major Project. Update by opening each project, removing the type and selecting the correct one, then Save & Close.
2. Set project ownership
Re-assign each project to the project manager who owns it in Altus by setting the Project Manager lookup. The Altus security model uses this to scope edit access.
3. Update remaining project information
Suggested fields to update post-migration: Description, Department, Target Finish, Investment Category, Location, Portfolio/Program, Sponsor, Status.
4. Set the current project stage
Each new project starts at the first stage of the Business Process Flow. Walk projects forward through stages by completing the checklist items and selecting Next Stage.
5. Update remaining bookable resource information
Suggested fields: Primary Role, Enterprise Calendar, Location, Start/End Date, Calendar Exceptions.
SharePoint data migration
SharePoint list data
Important
The target Altus projects must already exist before running the SharePoint list migration. If not, complete the Project Online migration first.
The list migration script supports UPSERTs and can be re-run safely. Deletions are not propagated — if you delete a list item in SharePoint, you must remove the corresponding Altus record manually.
By default the scripts map the Risks and Issues lists (see Migration Reference § Risks and § Issues for the OOTB mapping).
1. Update export.config.json
Add a script block per custom field. Only these fields need to change:
| Attribute | Details |
|---|---|
spFieldInternalName |
SharePoint internal name (set when the field was created; spaces become _x0020_). |
entityAttribute |
Logical name of the Altus column. |
type |
SharePoint type — Text, Bool, DateTime, DateOnly, Decimal, Number, Lookup, Money, OptionSet. |
choiceMap (Choice only) |
Map of SharePoint label → Altus option set value (e.g. 955000000). |
lookupEntity (Lookup only) |
Logical name of the related Altus table. |
Example:
{
"_fielddetails": "Single line of Text",
"spFieldInternalName": "ExampleTextField",
"entityAttribute": "ce_examplesptextfield",
"type": "Text"
}
See Migration Reference § Risks/Issues – Modified Script for the full set of field-type templates (Text, Bool, DateTime, DateOnly, Decimal, Number, Lookup, Money, OptionSet).
2. Run the export
.\1-export-lists.ps1 `
-SiteCollectionUrl "<pol url>" `
-MappingJsonPath ".\export.config.json" `
-OutputFolder ".\Output" `
-POLExportPath "<path to project Files>"
3. Run the import
.\2-import-lists.ps1 `
-D365Url "<altus url>" `
-DataFolder ".\Output" `
-POLExportPath "<path to project Files>"
Custom lists
Custom lists are configured in export.config.json alongside Risks and Issues. Each custom list entry specifies the SharePoint list title, the target Altus entity, the project lookup attribute, and a columnMap of field mappings. 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" }
]
}
SharePoint documents
What is migrated?
Documents are downloaded from each project's SharePoint subsite, including sub-folders, metadata tags, and version history (when enabled).
What is not migrated?
.aspx files are excluded by default; system libraries (Style Library, Preservation Hold Library, Form Templates, Recycle Bin, Site Assets) are excluded by default.
1. Create or identify the target library
Choose one of:
- A Teams channel for the Altus project.
- An existing non-Project-Online SharePoint document library.
- A new non-Project-Online SharePoint document library.
2. Update and run the export script
Configurable parameters:
| Parameter | Default | Notes |
|---|---|---|
-ExcludeVersionHistory |
$true |
Set $false to export 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. Remove an entry to include that library. |
-ExcludeFilePatterns |
@("*.aspx") |
Add additional patterns to exclude more file types. |
-IncludeRootWeb |
(switch) | Include the root web in the export. |
3. Complete post-migration steps
Upload the exported files to the target Teams channel or document library and verify metadata as required.
Related
- Migration Reference — full set of code blocks for extending each script.
- Troubleshooting — known issues and resolutions.
- Frequently Asked Questions