Creating a TFS Team Project Collection using Powershell

Grant Holiday posted on how to create a TPC using Powershell. However his post did not address how to set the SharePoint or Reporting Services parameters. His example used for the form

Dictionary<string, string> servicingTokens = new Dictionary<string, string>();
servicingTokens.Add("SharePointAction", "None"); // don't configure sharepoint
servicingTokens.Add("ReportingAction", "None"); // don't configure reporting services

So not much use if like ourselves you have your TFS integrated with the company wide SharePoint farm

Finding the Parameters (the hard way)

If you want configure SharePoint or Reporting Services it turns out there is not source of documentation for the process. So thanks then to Chris Sidi for telling me how to find out the required options. This is his process to find out what parameters a TPC needs to be created with:

  1. Create a TPC via the TFS Administration Console, specifying a custom configuration for Sharepoint and Reporting for your system.
  2. In the Administration Console at the bottom of the screen, switch to the Status tab. Double-click “Create Collection” to load the servicing log.
  3. In the first ~15 lines of the servicing log, find the JobId (e.g. “Setting token. Key: JobId. Value: 9638bd57-f494-4ac3-a073-dd1548ab24dc.”)
  4. Using Powershell, query the servicing tokens used:

[Reflection.Assembly]::Load("Microsoft.TeamFoundation.Client, Version=, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")
$credProvider = new-object Microsoft.TeamFoundation.Client.UICredentialsProvider
#Update the TFS url as necessary
$tfsConnection = new-object Microsoft.TeamFoundation.Client.TfsConfigurationServer "
http://localhost:8080/tfs", $credProvider
$jobService = $tfsConnection.GetService([Microsoft.TeamFoundation.Framework.Client.ITeamFoundationJobService])

# Replace the JobId. Use the one in the servicing log.
$jobId = '9638bd57-f494-4ac3-a073-dd1548ab24dc'
$servicingJob = $jobService.QueryJobs([Guid[]] @($jobId))[0]

This script gets the list of parameters as shown below which we can pass into the PowerShell script used to create a new  TPC.

SharePointAction UseExistingSite
SharePointServer 3eYRYkJOok6GHrKam0AcSA==wytV0xS6vE2uow3gjrzAEg==
SharePointSitePath sites/test
ReportingAction CreateFolder
ReportServer 3eYRYkJOok6GHrKam0AcSA==KRCi2RTWBk6Cl1wAphaxWA==
ReportFolder /TfsReports/test

It is shame the SharePoint and Reporting Services servers are returned as hash codes not their URLs, but as these will probably be fixed for any TFS implementation this is not a major issue as they can just be hardcoded.

Finding the Parameters (the easy way)

Once I ran this script I actually noticed that I already had access to this information without running  PowerShell or creating any trial TPCs. Isn’t that so often the case the information is under your nose but you don’t recognise it.

Actually all the parameters are actually shown at the start of the creation log for any TPC creation on a given TFS server. Just look for the log files via the TFS admin console.

[Info   @11:12:34.876] [2011-05-04 20:32:36Z][Informational] Creating dictionary with 14 initial tokens:
[Info   @11:12:34.876] [2011-05-04 20:32:36Z][Informational]     FinalHostState => Started
[Info   @11:12:34.876] [2011-05-04 20:32:36Z][Informational]     VMM_SHARES (Value is an empty string.)
[Info   @11:12:34.876] [2011-05-04 20:32:36Z][Informational]     VMM_HOSTS (Value is an empty string.)
[Info   @11:12:34.876] [2011-05-04 20:32:36Z][Informational]     INT_USRACC (Value is an empty string.)
[Info   @11:12:34.876] [2011-05-04 20:32:36Z][Informational]     SharePointAction => UseExistingSite
[Info   @11:12:34.876] [2011-05-04 20:32:36Z][Informational]     SharePointServer => 3eYRYkJOok6GHrKam0AcSA==wytV0xS6vE2uow3gjrzAEg==
[Info   @11:12:34.876] [2011-05-04 20:32:36Z][Informational]     SharePointSitePath => sites/test
[Info   @11:12:34.876] [2011-05-04 20:32:36Z][Informational]     ReportingAction => CreateFolder
[Info   @11:12:34.876] [2011-05-04 20:32:36Z][Informational]     ReportServer => 3eYRYkJOok6GHrKam0AcSA==KRCi2RTWBk6Cl1wAphaxWA==
[Info   @11:12:34.876] [2011-05-04 20:32:36Z][Informational]     ReportFolder => /TfsReports/test
[Info   @11:12:34.876] [2011-05-04 20:32:36Z][Informational]     DataTierConnectionString => Data Source=sqlserver;Initial Catalog=Tfs_Configuration;Integrated Security=True
[Info   @11:12:34.876] [2011-05-04 20:32:36Z][Informational]     CollectionName => test
[Info   @11:12:34.876] [2011-05-04 20:32:37Z][Informational]     InstanceId => 0cf30e1d-d8a6-4265-a854-9f6c5d288f7e
[Info   @11:12:34.876] [2011-05-04 20:32:37Z][Informational]     DefaultDatabase => Data Source=sqlserver;Initial Catalog=Tfs_test;Integrated Security=True

The revised TPC creation script

So given this new set of parameters we can edit Grant’s script to pass in all the extra parameters as shown below

    [string]$tpcName = $( throw "Missing: parameter tpcname"),
    [string]$serverUrl = http://localhost:8080/tfs/ ,
    [string]$sqlServer = "sqlserver" ,
    [string]$spBase = "sites", 
    [string]$spServer = "3eYRYkJOok6GHrKam0AcSA==wytV0xS6vE2uow3gjrzAEg==", 
    [string]$rsBase = "TfsReports",
    [string]$rsServer = "3eYRYkJOok6GHrKam0AcSA==KRCi2RTWBk6Cl1wAphaxWA==" )

Write-Host "Using the TPC with the following settings"
Write-Host "tpcName:   $tpcName"
Write-Host "serverUrl: $serverUrl"
Write-Host "sqlServer: $sqlServer"
Write-Host "spBase:    $spBase"
Write-Host "spServer:  $spServer"
Write-Host "rsBase:    $rsBase"
Write-Host "rsServer:  $rsServer"

# Load client OM assembly.
[Reflection.Assembly]::Load("Microsoft.TeamFoundation.Client, Version=, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a");

# Get the server
$tfsServer = new-object Microsoft.TeamFoundation.Client.TfsConfigurationServer $serverUrl;

# Create the token set
$servicingTokens = New-Object "System.Collections.Generic.Dictionary“2[System.String,System.String]"

$spPath = $spBase +"/" +  $tpcName;
$spPath = "customers/test";
Write-Host "Sharepoint path is  $spPath"
$servicingTokens.Add("SharePointAction", "UseExistingSite");
$servicingTokens.Add("SharePointServer", $spServer);
$servicingTokens.Add("SharePointSitePath", $spPath);

$rsPath = "/" + $rsBase + "/" + $tpcName
Write-Host "Reporting Services path is  $rsPath"
$servicingTokens.Add("ReportingAction", "CreateFolder");
$servicingTokens.Add("ReportServer", $rsServer);
$servicingTokens.Add("ReportFolder", $rsPath);

# Create and run the job
$tpcSvc = $tfsServer.GetService([Microsoft.TeamFoundation.Framework.Client.ITeamProjectCollectionService]);

$sqlString ="Server=$sqlServer;Integrated Security=SSPI;"
Write-Host "SQL connection string is  $sqlString "

$job = $tpcSvc.QueueCreateCollection(
    $tpcName,      # collection name.
    "",                  # description.
    $false,              # don't make this the default collection.
    "~/" + $tpcName + "/",   # virtual directory.
    "Started",           # State after creation.
    $servicingTokens,               # tokens for other services.
    $sqlString,       # The SQL instance to create the collection on. Specify SERVER\INSTANCE if not using default instance
    $null,               # null because the collection database doesn't already exist.
    $null)               # null because the collection database doesn't already exist.

Write-Host "Creating TPC (this could take a few minutes)"
$collection = $tpcSvc.WaitForCollectionServicingToComplete($job)
Write-Host "Compeleted"

If you get any errors from the script the best place to go is the TFS Admin console to look at the logs (the same ones you used to find the parameters). You should see the detailed create process log that gives normal TF errors as to why the process has failed; usually rights on SharePoint for Reporting Services from my experience.