
In this article I will show how you can use Powershell to download Reporting Services reports in bulk, how to upload reports and set their shared datasources in bulk, and how to query the report server’s contents such as listing the locations of all linked reports that reference a particular report.
Introduction
While the SQL Server Reporting Services (SSRS) web interface is fine for managing individual reports, in general it lacks the functionality to perform these operations in bulk. Also, some information simply cannot be obtained through the user interface, such as which linked reports reference a given report.
Luckily, SSRS provides a soap webservice, which allows SSRS to be managed programmatically, for example, through Powershell.
Connecting to the Report Server webservice
For invoking SOAP webservices, Powershell provides a very powerful cmdlet called New-WebServiceProxy which basically uses the services’ wsdl to generate a strongly-typed webservice client on the fly. For example, to connect to the local report server instance using the credentials of the user that’s running the script:
$reportServerUri = "http://localhost/reportserver/ReportService2010.asmx?wsdl" $rs = New-WebServiceProxy -Uri $reportServerUri -UseDefaultCredential -Namespace "SSRS"
There are a couple of things to note here:
- The url references the
/reportserver
, not the web UI which is on/reports
. - Even though the url contains
2010
, it’s still the latest version (at least for SQL Server 2014) - By specifying
-Namespace "SSRS"
, we tell it to place all generated types from the wsdl into a .NET namespace called SSRS. This is useful when we need to instantiate some of these types ourselves – we can then “new” them up by prefixing the wsdl type with this namespace, i.e. “New-Object SSRS.DataSourceReference
“
Querying for (linked) reports
Once the proxy has been created, we’re free to query at will – provided that your credentials are authorized to do so, of course:
# List everything(!) on the Report Server, recursively $catalogItems = $rs.ListChildren("/", $true) $catalogItems # List all Linked Reports, together with the path of the Report it refers to. $linkedReports = $rs.ListChildren("/", $true) | Where-Object { $_.TypeName -eq "LinkedReport" } $results = $linkedReports | Foreach-Object { $linkPath = $rs.GetItemLink($_.Path) $result = new-object PSObject -Property @{ LinkName = $_.Name; LinkPath = $_.Path; ReportPath = $linkPath } $result } $results # List all Linked Reports that refer to reports in a specific folder: $results | Where-Object { $_.ReportPath -like "/Reports/MyDeparment/*" }
Downloading multiple reports
Quickly downloading all reports from a particular folder as .rdl files is now a piece of cake as well:
# Download all Reports from a specific folder to .rdl files in the current # directory. $sourceFolderPath = "/Reports/MyDeparment" $items = $rs.ListChildren($sourceFolderPath, $false) $items | Where-Object { $_.TypeName -eq "Report" } | Foreach-Object { $filename = ("{0}.rdl" -f $_.Name) Write-Output ("Downloading ""{0}""..." -f $_.Path) $bytes = $rs.GetItemDefinition($_.Path) [System.IO.File]::WriteAllBytes("$pwd\$filename", $bytes) }
Uploading multiple reports
However, the power of Powershell (ha!) really starts to show when you want to upload multiple reports at once – and for good measure, we’ll also set the shared data source for each Report to an (already existing) datasource.
# Upload all .rdl files in the current directory to a specific folder, and # set their datasource references to the same shared datasource (should # already be deployed). $targetFolderPath = "/Reports/MyNewReports" $targetDatasourceRef = "/Data Sources/mySharedDataSource" $warnings = $null Get-ChildItem *.rdl | Foreach-Object { $reportName = [System.IO.Path]::GetFileNameWithoutExtension($_.Name) $bytes = [System.IO.File]::ReadAllBytes($_.FullName) Write-Output "Uploading report ""$reportName"" to ""$targetFolderPath""..." $report = $rs.CreateCatalogItem( "Report", # Catalog item type $reportName, # Report name $targetFolderPath,# Destination folder $true, # Overwrite report if it exists? $bytes, # .rdl file contents $null, # Properties to set. [ref]$warnings) # Warnings that occured while uploading. $warnings | ForEach-Object { Write-Output ("Warning: {0}" -f $_.Message) } # Get the (first) *design-time* name of the data sources that the # uploaded report references. Note that this might be different from # the name of the datasource as it is deployed on the report server! $referencedDataSourceName = (@($rs.GetItemReferences($report.Path, "DataSource")))[0].Name # Change the datasource for the report to $targetDatasourceRef # Note that we can access the types such as DataSource with the prefix # "SSRS" only because we specified that as our namespace when we # created the proxy with New-WebServiceProxy. $dataSource = New-Object SSRS.DataSource $dataSource.Name = $referencedDataSourceName # Name as used when designing the Report $dataSource.Item = New-Object SSRS.DataSourceReference $dataSource.Item.Reference = $targetDatasourceRef # Path to the shared data source as it is deployed here. $rs.SetItemDataSources($report.Path, [SSRS.DataSource[]]$dataSource) }
Summary
Obviously, there’s a lot more that you can accomplish using Reporting Service’s webservice. You can find the complete list of operations here, and categorized by usage here.
Cheers!
19 comments
This is perfect!! I’m working in 4 different SSRS environments with the same reports to deploy. This really helped the my automated deployment process.
Thank you so much!!!
–Sven
Sven
Good to hear, thanks for your reply Sven!
Léon Bouquiet
Awesome thank you very much
Diego Valenzuela
This has save me a ton of time. Thanks very much for sharing.
JustMike
thank you so much for this! We spend way too much time manually updating the Report Manager site with reports.
Sandy
You don’t want to link a report datasource to a Shared Datasource if the datasource in the report is embedded (DataSourceDefinition vs. DataSourceReference), right? How do you check that the item datasource type is DataSourceReference? I’ve been banging my head against a wall trying to do this. I’m pointing at a report where the XML clearly says it’s a Reference and when I inspect the Type of the Item returned by GetItemReferences it always says DataSourceDefinition!
Richard Schaefer
Ah… You’re using GetItemReferences, not GetItemDataSources. I get it. Never mind.
Richard Schaefer
Hi,
I have tried using the above code, but I get an error message when setting the data source. I can not figure out.
Cannot convert argument “DataSources”, with value: “SSRS.DataSource[]”, for “SetItemDataSources” to type “SSRS.DataSource[]”: “Cannot convert the
“SSRS.DataSource” value of type “SSRS.DataSource” to type “SSRS.DataSource”.
Any suggestions or help?
Scott
Most likely you’ve executed “New-WebServiceProxy” more than once in this Powershell session; Because of the “-Namespace SSRS” it has generated new types but with the exact same names, and Powershell gets confused. The easiest workaround is to start a new Powershell session (i.e. open a new Powershell (ISE) window).
Léon Bouquiet
I ran into the same thing. This post has a nice solution: http://www.sqlmusings.com/2012/02/04/resolving-ssrs-and-powershell-new-webserviceproxy-namespace-issue/
Frenk
Thank you for these scripts. How can I specify a path and folder for downloading reports and for the source of the uploading script?
Doug
The examples provided all use $pwd (i.e. the current working directory) as the directory to read from/write to, you could change that to any path you like.
Léon Bouquiet
This article saved me a lot of time when migrating a SSRS instance, and is very well written.
Thank you!
Pontus Sjöström
This article was of great help for me but I noticed that the new site template placed all code on one line and thus ruined formatting and readability.
Vincent
I was trying to consolidate a few SSRS Servers hosting over two thousand reports onto one server and this script has proved extremely helpful.
Thanks Léon- you are a champ.
Ishan Bhalla
Thank you very much for this code example. It saved me much time.
Mazin Asmar
Very well explained and exactly what i was looking for. Everything works great but in my case, we have shared data set which this script is not mapping reports to it.
A
Thanks you so very much! Really appreciate all of the knowledge shared.
Thanks,
Tom
Tom
You have long-lasting code. It’s still simple and perfect. Saved my time.
Saravana