powershellproject-serverpsi

PSI update lookup table on Project Server - error "LookupTableAlreadyExists"


I am working on a script, which shall update Enterprise Custom Fields and Lookup Tables for Project Server 2013, using PSI. The script gets the settings from XMLfiles which were exported from one system and then updates the settings on another system.

The procedure is the following: The skript gets the current LookupTableDataSet from the target system. It then adds, modifies or deletes lookup tables, entries and masks on that dataset. Builtin lookup tables were not modified in any way. Finally it passes the modified dataset to the UpdateLookupTables method to update the target system.

Problem: The target system keeps returning an "LookupTableAlreadyExists" error. It does this for all lookup tables that have been modified or not changed. Lookup tables that were deleted or added to not throw this error.

Here is the code:

$defaultLookupsEN = @("Cost Type", "Department", "Health", "RBS", "Relative Importance", "Project Impact")

function updateLookupTables($dataset) {
  $importPath = "$inXMLPath\LookupTables.xml"
  $lookupTablesXML = Import-Clixml $importPath

  $existingLookupTableNames = $dataset.LookupTables.Rows.LT_NAME
  $existingLookupTablesFullData = $dataset
  $existingTableIDs = $dataset.LookupTables.Rows.LT_UID

  foreach ($lookupTable in $lookupTablesXML) {
    if ($existingLookupTablesFullData.LookupTables.LT_UID.Contains($lookupTable.LT_UID)) {
        # if source table already exists on target system, update existing table

        # Do not update built-in lookup tables
        if($lcid -eq 1031 -and $defaultLookupsDE.Contains($lookupTable.LT_NAME)){
            continue;
        }
        if($lcid -eq 1033 -and $defaultLookupsEN.Contains($lookupTable.LT_NAME)){
            continue;
        }

        # Get current lookup table by UID
        $lookupRow = $existingLookupTablesFullData.LookupTables.Rows | ? {$_.LT_UID -eq $lookupTable.LT_UID}

        #add lookup values from xml
        $lookupRow.LT_NAME = [string] $lookupTable.LT_NAME
        $lookupRow.LT_SORT_ORDER_ENUM = [int] $lookupTable.LT_SORT_ORDER_ENUM
        $lookupRow.LT_PRIMARY_LCID = [int] $lookupTable.LT_PRIMARY_LCID
        $lookupRow.LT_FILL_ALL_LEVELS = [boolean] $lookupTable.LT_FILL_ALL_LEVELS

    } else {
        # if source table does not exists on target system, create new table

        # Do not update built in lookup tables
        if($lcid -eq 1031 -and $defaultLookupsDE.Contains($lookupTable.LT_NAME)){
            continue;
        }
        if($lcid -eq 1033 -and $defaultLookupsEN.Contains($lookupTable.LT_NAME)){
            continue;
        }

        $lookupRow = $existingLookupTablesFullData.LookupTables.NewLookupTablesRow();

        #add lookup values from xml
        $lookupRow.LT_UID = [Guid] $lookupTable.LT_UID
        $lookupRow.LT_NAME = [string] $lookupTable.LT_NAME
        $lookupRow.LT_SORT_ORDER_ENUM = [int] $lookupTable.LT_SORT_ORDER_ENUM
        $lookupRow.LT_PRIMARY_LCID = [int] $lookupTable.LT_PRIMARY_LCID
        $lookupRow.LT_FILL_ALL_LEVELS = [boolean] $lookupTable.LT_FILL_ALL_LEVELS

        $existingLookupTablesFullData.LookupTables.AddLookupTablesRow($lookupRow);

        $ltName = $lookupRow.LT_NAME
    }
  }

  for($i = 0; $i -lt $existingLookupTablesFullData.LookupTables.LT_UID.Length; $i++) {
    # remove Lookup tables which are not in source file
    if(!$lookupTablesXML.LT_UID.Contains($existingLookupTablesFullData.LookupTables.Rows.LT_UID[$i])) {
        $existingLookupTablesFullData.LookupTables.Rows[$i].delete()
    }
  }
}

function writeLookupTableDataset($lds) {
    $lookupSvc.UpdateLookupTables($lds, $false, $true, $lcid); 
}

function readExistingLookupTablesDS(){
    return $lookupSvc.ReadLookupTables([system.string]::empty , 0 , $lcid);
}


#------------------- Main Function ---------------------------------

$tablesTarget = readExistingLookupTablesDS

#update the lookup tables and add the rows to the dataset 
updateLookupMasks $tablesTarget
updateLookupEntries $tablesTarget
updateLookupTables $tablesTarget
writeLookupTableDataset $tablesTarget

Here is a readout from the lookup tables:

$lds.LookupTables.Rows | select LT_NAME, RowState
LT_NAME              RowState
-------              --------
                      Deleted
Cost Category        Modified    -Error
Cost Origin          Modified    -Error
Cost Rate Currency   Modified    -Error
Internal             Modified    -Error
Cost Account         Modified    -Error
Project Impact      Unchanged    -Error
Department          Unchanged    -Error
                      Deleted
Cost Type           Unchanged    -Error
Health              Unchanged    -Error
RBS                 Unchanged    -Error
Relative Importance Unchanged    -Error
Domains                 Added
Test LT                 Added

Here the Error message (I included the names of the lookup tables at the beginning of each line for better understanding):

<errinfo>
<dataset name="LookupTableDataSet">
<table name="LookupTableMasks">
Cost Category       <row LT_UID="dc2f5020-beae-e911-842b-00155dc61919" LT_MASK_STRUCT_LEVEL="1"><error id="11076" name="LookupTableAlreadyExists" uid="bbe7bf04-abc8-e911-842b-00155dc61919" /></row>
Cost Origin         <row LT_UID="85417926-beae-e911-842b-00155dc61919" LT_MASK_STRUCT_LEVEL="1"><error id="11076" name="LookupTableAlreadyExists" uid="bce7bf04-abc8-e911-842b-00155dc61919" /></row>
Cost Rate           <row LT_UID="8986cd2f-beae-e911-842b-00155dc61919" LT_MASK_STRUCT_LEVEL="1"><error id="11076" name="LookupTableAlreadyExists" uid="bde7bf04-abc8-e911-842b-00155dc61919" /></row>
Internal            <row LT_UID="2215a93c-beae-e911-842b-00155dc61919" LT_MASK_STRUCT_LEVEL="1"><error id="11076" name="LookupTableAlreadyExists" uid="bee7bf04-abc8-e911-842b-00155dc61919" /></row>
Cost Account        <row LT_UID="8b6cfb6a-beae-e911-842b-00155dc61919" LT_MASK_STRUCT_LEVEL="1"><error id="11076" name="LookupTableAlreadyExists" uid="bfe7bf04-abc8-e911-842b-00155dc61919" /></row>
Project Impact      <row LT_UID="e8bcec5d-8707-4048-8ff0-4d386457edf7" LT_MASK_STRUCT_LEVEL="1"><error id="11076" name="LookupTableAlreadyExists" uid="c0e7bf04-abc8-e911-842b-00155dc61919" /></row>
Department          <row LT_UID="e7397277-1ab0-4096-b2dd-57029a055ba4" LT_MASK_STRUCT_LEVEL="1"><error id="11076" name="LookupTableAlreadyExists" uid="c1e7bf04-abc8-e911-842b-00155dc61919" /></row>
Cost Type           <row LT_UID="0000e630-4965-42b3-a697-88f060891b22" LT_MASK_STRUCT_LEVEL="1"><error id="11076" name="LookupTableAlreadyExists" uid="c2e7bf04-abc8-e911-842b-00155dc61919" /></row>
Health              <row LT_UID="0000a4aa-160e-499a-905f-d498472dfb35" LT_MASK_STRUCT_LEVEL="1"><error id="11076" name="LookupTableAlreadyExists" uid="c3e7bf04-abc8-e911-842b-00155dc61919" /></row>
RBS                 <row LT_UID="00008e67-65a3-4898-baaa-d82d995bbb02" LT_MASK_STRUCT_LEVEL="1"><error id="11076" name="LookupTableAlreadyExists" uid="c4e7bf04-abc8-e911-842b-00155dc61919" /></row>
Relative Importance <row LT_UID="5ad95f2a-cb3e-43d4-b42b-faf2db138590" LT_MASK_STRUCT_LEVEL="1"><error id="11076" name="LookupTableAlreadyExists" uid="c5e7bf04-abc8-e911-842b-00155dc61919" /></row>
</table>
</dataset>
</errinfo>

Solution

  • The error was in the updateLookupTableMasks function. To be more efficient the function originally deleted all the lookup table masks and created new ones with the updated information. It turns out that this leads to the error. The lookup table masks had to be modified in place.

    Therefore my solution was to write an additional function, which checks if a lookup table mask already exists on the server and only add a new one if it does not.