Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Step template to deploy SSRS reports #150

Merged
merged 2 commits into from
May 25, 2015
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 65 additions & 0 deletions step-templates/ssrs-deploy-from-package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
{
"Id": "ActionTemplates-34",
"Name": "Deploy SSRS Reports from a package",
"Description": "Uploads SSRS reports to an SSRS server from a NuGet package.",
"ActionType": "Octopus.Script",
"Version": 0,
"Properties": {
"Octopus.Action.Script.ScriptBody": "\r\n$DeployedPath = $OctopusParameters[\"Octopus.Action[$NugetPackageStepName].Output.Package.InstallationDirectoryPath\"]\r\n\r\n#region Upload-Report()\r\nFunction Upload-Report([string] $ReportFile)\r\n{\r\n Write-Host \"Loading binary data from $ReportFile\"\r\n $ReportData = [System.IO.File]::ReadAllBytes($ReportFile)\r\n\r\n # Create local variables\r\n $ReportServiceWarnings = $null\r\n $ReportName = $ReportFile.SubString($ReportFile.LastIndexOf(\"\\\") + 1)\r\n $ReportName = $ReportName.SubString(0, $ReportName.IndexOf(\".\"))\r\n\r\n # Upload report\r\n Write-Host \"Uploading $ReportName\"\r\n $Result = $ReportServerProxy.CreateCatalogItem(\"Report\", $ReportName, $ReportFolder, $true, $ReportData, $null, [ref] $ReportServiceWarnings);\r\n}\r\n#endregion\r\n\r\n#region Get-ReportDataSourceNames()\r\nFunction Get-ReportDataSourceNames([string] $ReportFile)\r\n{\r\n # declare working variables\r\n $DataSourceNames = @()\r\n \r\n # load the xml\r\n [xml]$ReportXml = Get-Content $ReportFile\r\n\r\n # retrieve the datasource nodes\r\n $DataSourceReferenceNodes = $ReportXml.GetElementsByTagName(\"DataSourceReference\")\r\n\r\n # loop through returned results\r\n foreach($Node in $DataSourceReferenceNodes)\r\n {\r\n # store the name\r\n $DataSourceNames += $Node.InnerText\r\n }\r\n\r\n # return the results\r\n return ,$DataSourceNames # Apparently using the , in front of the variable is how you return explicit arrays in PowerShell ... could you be more obsure?\r\n}\r\n#endregion\r\n\r\n#region Item-Exists()\r\nFunction Item-Exists($ItemFolderPath, $ItemName)\r\n{\r\n # declare search condition\r\n $SearchCondition = New-Object \"$ReportServerProxyNamespace.SearchCondition\";\r\n\r\n # fill in properties\r\n $SearchCondition.Condition = Get-SpecificEnumValue -EnumNamespace \"$ReportServerProxyNamespace.ConditionEnum\" -EnumName \"Equals\"\r\n $SearchCondition.ConditionSpecified = $true\r\n $SearchCondition.Name = \"Name\"\r\n $SearchCondition.Values = @($ItemName)\r\n\r\n # search\r\n $items = $ReportServerProxy.FindItems($ItemFolderPath, (Get-SpecificEnumValue -EnumNamespace \"$ReportServerProxyNamespace.BooleanOperatorEnum\" -EnumName \"And\"), $null, $SearchCondition)\r\n \r\n # check to see if anything was returned\r\n if($items.Length -gt 0)\r\n {\r\n # return true\r\n return $true\r\n }\r\n else\r\n {\r\n return $false\r\n }\r\n}\r\n#endregion\r\n\r\n#region Set-ReportDataSources()\r\nFunction Set-ReportDataSources($ReportDataSourceNames, $ReportFile)\r\n{\r\n # declare local variables\r\n $ReportName = $ReportFile.SubString($ReportFile.LastIndexOf(\"\\\") + 1)\r\n $ReportName = $ReportName.SubString(0, $ReportName.IndexOf(\".\"))\r\n $AllDataSourcesFound = $true\r\n\r\n # get the datasources\r\n $DataSources = $ReportServerProxy.GetItemDataSources([string]::Format(\"{0}/{1}\", $ReportFolder, $ReportName))\r\n\r\n for($i = 0; $i -lt $DataSources.Length; $i++)\r\n {\r\n # get the current datasource name\r\n $ReportDataSourceName = $ReportDataSourceNames[$i]\r\n\r\n # check to make sure the datasource exists in the location specified\r\n if((Item-Exists -ItemFolderPath $ReportDatasourceFolder -ItemName $ReportDataSourceNames[$i]) -eq $true)\r\n {\r\n # create datasource reference variable\r\n $DataSourceReference = New-Object \"$ReportServerProxyNamespace.DataSourceReference\";\r\n\r\n # assign\r\n $DataSourceReference.Reference = \"$ReportDatasourceFolder/$ReportDataSourceName\"\r\n $DataSources[$i].Item = $DataSourceReference\r\n }\r\n else\r\n {\r\n # display warning\r\n Write-Warning \"Warning - unable to find datasource $ReportDataSourceName in $ReportDataSourceFolder\"\r\n\r\n # update to false\r\n $AllDataSourcesFound = $false\r\n } \r\n }\r\n\r\n # check to see if found all datasources\r\n if($AllDataSourcesFound -eq $true)\r\n {\r\n Write-Host \"Associating datasources to $ReportFolder/$ReportName\"\r\n \r\n # save the references\r\n $ReportServerProxy.SetItemDataSources(\"$ReportFolder/$ReportName\", $DataSources)\r\n }\r\n}\r\n#endregion\r\n\r\n#region Get-ObjectNamespace()\r\nFunction Get-ObjectNamespace($Object)\r\n{\r\n # return the value\r\n ($Object).GetType().ToString().SubString(0, ($Object).GetType().ToString().LastIndexOf(\".\"))\r\n}\r\n#endregion\r\n\r\n#region Get-SpecificEnumValue()\r\nFunction Get-SpecificEnumValue($EnumNamespace, $EnumName)\r\n{\r\n # get the enum values\r\n $EnumValues = [Enum]::GetValues($EnumNamespace)\r\n\r\n # Loop through to find the specific value\r\n foreach($EnumValue in $EnumValues)\r\n {\r\n # check current\r\n if($EnumValue -eq $EnumName)\r\n {\r\n # return it\r\n return $EnumValue\r\n }\r\n }\r\n\r\n # nothing was found\r\n return $null\r\n}\r\n#endregion\r\n\r\n#region Update-ReportParamters()\r\nFunction Update-ReportParameters($ReportFile)\r\n{\r\n # declare local variables\r\n $ReportParameters = @();\r\n\r\n # necessary so that when attempting to use the report execution service, it doesn't puke on you when it can't find the data source\r\n $ReportData = (Remove-Datasources -ReportFile $ReportFile)\r\n\r\n # get just the report name\r\n $ReportName = $ReportFile.SubString($ReportFile.LastIndexOf(\"\\\") + 1)\r\n $ReportName = $ReportName.SubString(0, $ReportName.IndexOf(\".\"))\r\n \r\n # create warnings object\r\n $ReportExecutionWarnings = $null\r\n\r\n # load the report definition\r\n $ExecutionInfo = $ReportExecutionProxy.LoadReportDefinition($ReportData, [ref] $ReportExecutionWarnings);\r\n\r\n # loop through the report execution parameters\r\n foreach($Parameter in $ExecutionInfo.Parameters)\r\n {\r\n # create new item parameter object\r\n $ItemParameter = New-Object \"$ReportServerProxyNamespace.ItemParameter\";\r\n\r\n # fill in the properties except valid values, that one needs special processing\r\n Copy-ObjectProperties -SourceObject $Parameter -TargetObject $ItemParameter;\r\n\r\n # fill in the valid values\r\n $ItemParameter.ValidValues = Convert-ValidValues -SourceValidValues $Parameter.ValidValues;\r\n\r\n # add to list\r\n $ReportParameters += $ItemParameter;\r\n }\r\n\r\n # force the parameters to update\r\n Write-Host \"Updating report parameters for $ReportFolder/$ReportName\"\r\n $ReportServerProxy.SetItemParameters(\"$ReportFolder/$ReportName\", $ReportParameters);\r\n}\r\n#endregion\r\n\r\n#region Remove-Datasources()\r\nFunction Remove-Datasources($ReportFile)\r\n{\r\n ######################################################################################################\r\n #You'll notice that I've used the keywrod of [void] in front of some of these method calls, this is so\r\n #that the operation isn't captured as output of the function\r\n ######################################################################################################\r\n\r\n # read xml\r\n [xml]$ReportXml = Get-Content $ReportFile;\r\n \r\n # create new memory stream object\r\n $MemoryStream = New-Object System.IO.MemoryStream\r\n\r\n try\r\n {\r\n # get all datasource nodes\r\n $DatasourceNodes = $ReportXml.GetElementsByTagName(\"DataSourceReference\");\r\n\r\n $NodesToRemove = @();\r\n\r\n # loop through returned nodes\r\n foreach($DataSourceNode in $DatasourceNodes)\r\n {\r\n # create a new connection properties node\r\n $ConnectionProperties = $ReportXml.CreateNode($DataSourceNode.NodeType, \"ConnectionProperties\", $null);\r\n\r\n # create a new dataprovider node\r\n $DataProvider = $ReportXml.CreateNode($DataSourceNode.NodeType, \"DataProvider\", $null);\r\n $DataProvider.InnerText = \"SQL\";\r\n\r\n # create new connection string node\r\n $ConnectString = $ReportXml.CreateNode($DataSourceNode.NodeType, \"ConnectString\", $null);\r\n $ConnectString.InnerText = \"Data Source=Server Name Here;Initial Catalog=database name here\";\r\n\r\n # add new node to parent node\r\n [void] $DataSourceNode.ParentNode.AppendChild($ConnectionProperties);\r\n\r\n # append childeren\r\n [void] $ConnectionProperties.AppendChild($DataProvider);\r\n [void] $ConnectionProperties.AppendChild($ConnectString);\r\n\r\n # Add to remove list \r\n $NodesToRemove += $DataSourceNode;\r\n }\r\n\r\n # loop through nodes to remove\r\n foreach($Node in $NodesToRemove)\r\n {\r\n # remove from parent\r\n [void] $Node.ParentNode.RemoveChild($Node);\r\n }\r\n \r\n $ReportXml.InnerXml = $ReportXml.InnerXml.Replace(\"xmlns=`\"`\"\", \"\")\r\n\r\n # save altered xml to memory stream\r\n $ReportXml.Save($MemoryStream);\r\n\r\n # return the altered xml as byte array\r\n return $MemoryStream.ToArray();\r\n }\r\n finally\r\n {\r\n # close and dispose\r\n $MemoryStream.Close();\r\n $MemoryStream.Dispose();\r\n }\r\n}\r\n#endregion\r\n\r\n#region Copy-ObjectProperties()\r\nFunction Copy-ObjectProperties($SourceObject, $TargetObject)\r\n{\r\n # Get source object property array\r\n $SourcePropertyCollection = $SourceObject.GetType().GetProperties();\r\n\r\n # get the destination\r\n $TargetPropertyCollection = $TargetObject.GetType().GetProperties();\r\n\r\n # loop through source property collection\r\n for($i = 0; $i -lt $SourcePropertyCollection.Length; $i++)\r\n {\r\n # get the target property\r\n $TargetProperty = $TargetPropertyCollection | Where {$_.Name -eq $SourcePropertyCollection[$i].Name}\r\n \r\n # check to see if it's null\r\n if($TargetProperty -ne $null)\r\n {\r\n # check to see if it's the valid values property\r\n if($TargetProperty.Name -ne \"ValidValues\")\r\n {\r\n # set the value\r\n $TargetProperty.SetValue($TargetObject, $SourcePropertyCollection[$i].GetValue($SourceObject));\r\n }\r\n }\r\n }\r\n}\r\n#endregion\r\n\r\n#region ConvertValidValues()\r\nFunction Convert-ValidValues($SourceValidValues)\r\n{\r\n # declare local values\r\n $TargetValidValues = @();\r\n \r\n # loop through source values\r\n foreach($SourceValidValue in $SourceValidValues)\r\n {\r\n # create target valid value object\r\n $TargetValidValue = New-Object \"$ReportServerProxyNamespace.ValidValue\";\r\n\r\n # copy properties\r\n Copy-ObjectProperties -SourceObject $SourceValidValue -TargetObject $TargetValidValue\r\n\r\n # add to list\r\n $TargetValidValues += $TargetValidValue\r\n }\r\n\r\n # return the values\r\n return ,$TargetValidValues\r\n}\r\n#endregion\r\n\r\n#region Backup-ExistingItem()\r\nFunction Backup-ExistingItem($ReportFile)\r\n{\r\n # declare local variables\r\n $ReportName = $ReportFile.SubString($ReportFile.LastIndexOf(\"\\\") + 1)\r\n $ReportName = $ReportName.SubString(0, $ReportName.IndexOf(\".\"))\r\n\r\n # check to see if the item exists\r\n if((Item-Exists -ItemFolderPath $ReportFolder -ItemName $ReportName) -eq $true)\r\n {\r\n # get file extension\r\n $ReportExtension = [System.IO.Path]::GetExtension($ReportFile)\r\n \r\n # check backuplocation\r\n if($BackupLocation.EndsWith(\"\\\") -ne $true)\r\n {\r\n # append ending slash\r\n $BackupLocation = $BackupLocation + \"\\\"\r\n }\r\n\r\n # ensure the backup location actually exists\r\n if((Test-Path $BackupLocation) -ne $true)\r\n {\r\n # create it\r\n New-Item -ItemType Directory -Path $BackupLocation\r\n }\r\n\r\n # download the item\r\n $Item = $ReportServerProxy.GetItemDefinition(\"$ReportFolder/$ReportName\")\r\n\r\n # form the backup path\r\n $BackupPath = \"{0}{1}{2}\" -f $BackupLocation, $ReportName, $ReportExtension;\r\n\r\n # write to disk\r\n [System.IO.File]::WriteAllBytes(\"$BackupPath\", $Item);\r\n\r\n # write to screen\r\n Write-Host \"Backed up $ReportFolder/$ReportName to $BackupPath\";\r\n }\r\n}\r\n#endregion\r\n\r\n#region Main\r\n\r\ntry\r\n{\r\n # declare array for reports\r\n $ReportFiles = @()\r\n\r\n # get all report files for deployment\r\n Write-Host \"Getting all .rdl files\"\r\n Get-ChildItem $DeployedPath -Recurse -Filter \"*.rdl\" | ForEach-Object { If(($ReportFiles -contains $_.FullName) -eq $false) {$ReportFiles += $_.FullName}}\r\n\r\n # set the report proxies\r\n Write-Host \"Creating SSRS Web Service proxies\"\r\n $ReportServerProxy = New-WebServiceProxy -Uri $ReportServerUrl -UseDefaultCredential \r\n $ReportExecutionProxy = New-WebServiceProxy -Uri $ReportExecutionUrl -UseDefaultCredential \r\n\r\n # get the service proxy namespaces - this is necessary because of a bug documented here http://stackoverflow.com/questions/7921040/error-calling-reportingservice2005-finditems-specifically-concerning-the-bool and http://www.vistax64.com/powershell/273120-bug-when-using-namespace-parameter-new-webserviceproxy.html\r\n $ReportServerProxyNamespace = Get-ObjectNamespace -Object $ReportServerProxy\r\n $ReportExecutionProxyNamespace = Get-ObjectNamespace -Object $ReportExecutionProxy\r\n\r\n # get the proxy auto generated name spaces\r\n\r\n # loop through array\r\n foreach($ReportFile in $ReportFiles)\r\n {\r\n # check to see if we need to back up\r\n if($BackupLocation -ne $null -and $BackupLocation -ne \"\")\r\n {\r\n # backup the item\r\n Backup-ExistingItem -ReportFile $ReportFile\r\n }\r\n \r\n # upload report\r\n Upload-Report $ReportFile\r\n\r\n # extract datasources\r\n Write-Host \"Extracting datasource names for $ReportFile\"\r\n $ReportDataSourceNames = Get-ReportDataSourceNames $ReportFile\r\n\r\n # set the datasources\r\n Set-ReportDataSources -ReportDataSourceNames $ReportDataSourceNames -ReportFile $ReportFile\r\n\r\n # update the report parameters\r\n Update-ReportParameters -ReportFile $ReportFile\r\n }\r\n}\r\nfinally\r\n{\r\n # check to see if the proxies are null\r\n if($ReportServerProxy -ne $null)\r\n {\r\n # dispose\r\n $ReportServerProxy.Dispose();\r\n }\r\n\r\n if($ReportExecutionProxy -ne $null)\r\n {\r\n # dispose\r\n $ReportExecutionProxy.Dispose();\r\n }\r\n}\r\n\r\n#endregion"
},
"SensitiveProperties": {},
"Parameters": [
{
"Name": "NugetPackageStepName",
"Label": "SSRS package step",
"HelpText": "Select the step in this project which downloads the SSRS package.",
"DefaultValue": null,
"DisplaySettings": {
"Octopus.ControlType": "StepName"
}
},
{
"Name": "ReportServerUrl",
"Label": "Url of SSRS Server service",
"HelpText": "The complete Url to the SSRS server.\nExample: http://198.239.216.46/ReportServer_LOCALDEV/reportservice2010.asmx?wsdl",
"DefaultValue": null,
"DisplaySettings": {
"Octopus.ControlType": "SingleLineText"
}
},
{
"Name": "ReportExecutionUrl",
"Label": "Report Execution Url",
"HelpText": "The complete Url to the Report Execution service.\nExample: http://198.239.216.46/ReportServer_LOCALDEV/ReportExecution2005.asmx?wsdl",
"DefaultValue": null,
"DisplaySettings": {
"Octopus.ControlType": "SingleLineText"
}
},
{
"Name": "ReportFolder",
"Label": "Report folder",
"HelpText": "Relative Url to the folder in which the reports will be deployed.",
"DefaultValue": null,
"DisplaySettings": {
"Octopus.ControlType": "SingleLineText"
}
},
{
"Name": "ReportDatasourceFolder",
"Label": "Report data source folder",
"HelpText": "Relative Url where the data sources for the reports are located.",
"DefaultValue": null,
"DisplaySettings": {
"Octopus.ControlType": "SingleLineText"
}
}
],
"LastModifiedOn": "2015-04-09T22:41:00.325+00:00",
"LastModifiedBy": "Shawn.Sesna@dshs.wa.lcl",
"$Meta": {
"ExportedAt": "2015-04-27T21:03:39.312Z",
"OctopusVersion": "2.6.5.1010",
"Type": "ActionTemplate"
}
}