Removing VSS Bindings to Migrate SSRS 2005 Solutions to TFS Using Powershell
The last task I have for a VSS to TFS Migration is to convert a large number of SSRS (SQL Server Reporting Services) Projects. While Microsoft has a tool to migrate from VSS to TFS - I've avoided using it. Most "experts" have advised against it for a number of reasons. Particularly, a desire to do some refactoring on the organization of the project folders, and just to give a "clean start" for TFS.
For this clients other applications, generally there are a reasonable number of solutions, so manual efforts, like un-binding from VSS and re-binding to TFS in Visual Studio has not been that big of a deal. Unfortunately my client has close to 100 SSRS projects and about half as many solutions. One of the Power Tools "tfpt.exe" supposedly automated re-binding to TFS - but it didn't work for me. I think it may only be intended to use in conjunction with the VSSConverter tool.
Another promising tool was suggested to me by fellow Magenicon-TFS-Expert Donn Felker. This tool claims to remove the binding, but it nuked the entire .sln file for me - and I don't know if it fixed the .rptproj (I got too frustrated to pursue it further) Donn mentioned having similar problems, but somehow got a workaround. I never figured it out and punted.
I decided to write my own PowerShell script to do it. My approach is to do a "Get-Latest" from VSS to it's working directory, then copy the files to the Workspace mapped Directory for TFS. Then run the script, then add the files to TFS. Here's what the script does:
- Clear the Read-Only flag on all the files
- Delete the files I don't want in TFS (*.suo, *.user, *.*scc)
- Remove the binding section from the sln files
- Remove the "State" from the .rptproj files
The last one was the most interesting discovery. SSRS Project Files are not MSBuild projects (like most other Visual Studio projects) but they are XML documents. They have a "State" element that has SOMETHING encoded. I've seen many references for SSRS issues that offer solution to "manually edit the .RPTProj and remove the "State" element. So I knew it was okay to delete them. I also discovered that through trial-and-error that this was necessary to remove the binding to VSS for the individual reports in each solution.
After the end of the post I have my PowerShell script that accomplishes it. It's not very fancy - without error detection etc - since this is a "One Shot" type thing - but it does what is needed. The only thing I'm unhappy about - I've been able to find ANYTHING as far as an automated "binding" of solutions to TFS. So my users will need to manually do this as they edit reports. Many haven't been touched since they were written in 2006 - so this is hopefully not a big deal...
Here's the script in all it's glory
$Folder = "C:\File13\StARS.Reports"
#first clear the read-only flag from all files
get-childitem "$folder" -Recurse | % {
# Test for ReadOnly flag and remove if present
if ($_.attributes -band [system.IO.FileAttributes]::ReadOnly) {
$_.attributes = $_.attributes -bxor [system.IO.FileAttributes]::ReadOnly
}
}
#next delete all files that are *.suo, *.user, and *.*scc - we don't want thim in TFS
Get-ChildItem $folder *.suo -Recurse -Force | Remove-Item -Force
Get-ChildItem $folder *.*scc -Recurse -Force | Remove-Item -Force
Get-ChildItem $folder *.user -Recurse -Force | Remove-Item -Force
#next get all the .sln file - and remove the VSS binding information
$files = Get-ChildItem $folder *.sln -Recurse
foreach ($file in $files) {
$fileout = $file.FullName + ".new"
Set-Content $fileout $null
$switch=0
Get-Content $file.FullName | % {
if ($switch -eq 0) {
if ($_ -eq " GlobalSection(SourceCodeControl) = preSolution") {
#we found the section to skip - so set the flag and don't copy the content
$switch=1}
else {
#we haven't found it yet - so copy the content
Add-Content $fileout $_
}
}
elseif ($switch -eq 1) {
if ($_ -eq " EndGlobalSection") {
#last line to skip - after it we start writing the content again
$switch=2}
}
else
{ #write remaining lines
Add-Content $fileout $_}
}
#remove the original .sln and rename the new one
$newname = $file.Name
Remove-Item $file.FullName
Rename-Item $fileout -NewName $newname
}
##next we need to remove the <State> Node from the .rptproj files - it also contains binding information
$files = Get-ChildItem $folder *.rptproj -Recurse
foreach ($file in $files) {
#read the project file as XML
$proj = [xml] [string]::Join("`n",
(get-content $file.FullName))
$stateNode = $proj.SelectNodes("/Project/State")
$root = $proj.Project
[Void]$root.RemoveChild($stateNode.item(0))
#save file todo
$proj.Save($file.FullName)
}