SSRS and PowerShell: Get report as Excel - powershell

I'm trying to make PowerShell send a web request to our SSRS server and capture the results. I've hit a wall using the rs:FORMAT=EXCEL parameter in the SSRS url string. I have the following:
First, init the credentials:
$User = "MYDOMAIN\MyUser"
$PWord = ConvertTo-SecureString -String "WooHooStringP$W0rd" -AsPlainText -Force
$c = New-Object –TypeName System.Management.Automation.PSCredential –ArgumentList $User, $PWord
Now, request a report:
Invoke-WebRequest `
-UserAgent ([Microsoft.PowerShell.Commands.PSUserAgent]::InternetExplorer) `
-Credential $c `
-Uri "http://myserver/ReportServer_DEV/Pages/ReportViewer.aspx?/folder+path/report+name"
This works fine. I can even grab the results (enclosing this request and using ().Content).
Then, specify a format instead of plain rendering:
Invoke-WebRequest `
-UserAgent ([Microsoft.PowerShell.Commands.PSUserAgent]::InternetExplorer) `
-Credential $c `
-Uri "http://myserver/ReportServer_DEV/Pages/ReportViewer.aspx?/folder+path/report+name&rs:format=HTML4.0"
Note the rs:Format specification? Works like a charm.
Then, for the grande finale, give me an Excel file:
Invoke-WebRequest `
-UserAgent ([Microsoft.PowerShell.Commands.PSUserAgent]::InternetExplorer) `
-Credential $c `
-Uri "http://myserver/ReportServer_DEV/Pages/ReportViewer.aspx?/folder+path/report+name&rs:format=EXCEL"
No can do, bud:
Invoke-WebRequest : The remote server returned an error: (401) Unauthorized.
At line:1 char:11
+ $bytez = (Invoke-WebRequest `
+ ~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-WebRequest], WebException
+ FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeWebRequestCommand
Why does the rs:format=EXCEL option throw an Unauthorised exception where all the other URLs are served by SSRS?

I've figured it out! I went about this the wrong way: SSRS offers access through a webservice that PowerShell can consume without the need to hack the URL and capture a response. I found a script that did this and modified it to suit my purpose:
function GetRSConnection($server, $instance)
{
# Create a proxy to the SSRS server and give it the namespace of 'RS' to use for
# instantiating objects later. This class will also be used to create a report
# object.
$User = "DOMAIN\Username"
$PWord = ConvertTo-SecureString -String "Pa$$w0rd" -AsPlainText -Force
$c = New-Object –TypeName System.Management.Automation.PSCredential –ArgumentList $User, $PWord
$reportServerURI = "http://" + $server + "/" + $instance + "/ReportExecution2005.asmx?WSDL"
$RS = New-WebServiceProxy -Class 'RS' -NameSpace 'RS' -Uri $reportServerURI -Credential $c
$RS.Url = $reportServerURI
return $RS
}
function GetReport($RS, $reportPath)
{
# Next we need to load the report. Since Powershell cannot pass a null string
# (it instead just passses ""), we have to use GetMethod / Invoke to call the
# function that returns the report object. This will load the report in the
# report server object, as well as create a report object that can be used to
# discover information about the report. It's not used in this code, but it can
# be used to discover information about what parameters are needed to execute
# the report.
$reportPath = "/" + $reportPath
$Report = $RS.GetType().GetMethod("LoadReport").Invoke($RS, #($reportPath, $null))
# initialise empty parameter holder
$parameters = #()
$RS.SetExecutionParameters($parameters, "nl-nl") > $null
return $report
}
function AddParameter($params, $name, $val)
{
$par = New-Object RS.ParameterValue
$par.Name = $name
$par.Value = $val
$params += $par
return ,$params
}
function GetReportInFormat($RS, $report, $params, $outputpath, $format)
{
# Set up some variables to hold referenced results from Render
$deviceInfo = "<DeviceInfo><NoHeader>True</NoHeader></DeviceInfo>"
$extension = ""
$mimeType = ""
$encoding = ""
$warnings = $null
$streamIDs = $null
# Report parameters are handled by creating an array of ParameterValue objects.
# Add the parameter array to the service. Note that this returns some
# information about the report that is about to be executed.
# $RS.SetExecutionParameters($parameters, "en-us") > $null
$RS.SetExecutionParameters($params, "nl-nl") > $null
# Render the report to a byte array. The first argument is the report format.
# The formats I've tested are: PDF, XML, CSV, WORD (.doc), EXCEL (.xls),
# IMAGE (.tif), MHTML (.mhtml).
$RenderOutput = $RS.Render($format,
$deviceInfo,
[ref] $extension,
[ref] $mimeType,
[ref] $encoding,
[ref] $warnings,
[ref] $streamIDs
)
# Determine file name
$parts = $report.ReportPath.Split("/")
$filename = $parts[-1] + "."
switch($format)
{
"EXCEL" { $filename = $filename + "xls" }
"WORD" { $filename = $filename + "doc" }
"IMAGE" { $filename = $filename + "tif" }
default { $filename = $filename + $format }
}
if($outputpath.EndsWith("\\"))
{
$filename = $outputpath + $filename
} else
{
$filename = $outputpath + "\" + $filename
}
$filename
# Convert array bytes to file and write
$Stream = New-Object System.IO.FileStream($filename), Create, Write
$Stream.Write($RenderOutput, 0, $RenderOutput.Length)
$Stream.Close()
}
$RS = GetRSConnection -server "DEVBOX" -instance "ReportServer_DEV"
$report = GetReport -RS $RS -reportPath "folder name/report name"
$params = #()
$params = AddParameter -params $params -name "Month" -val "201311"
GetReportInformat -RS $RS -report $report -params $params -outputpath "i:\test" -format "EXCEL"

Using web request:
[string]$Domain = "DomainUsername"
[string]$Username = "Username"
[string]$Password = "Password"
[string]$ReportServer = "http://ssrsreportserver/ReportServer/ReportExecution2005.asmx" #Report Server
[string]$ReportLocation = "/Report Location/Report Name" #Report Location ON SSRS
$ReportLocation = $ReportLocation.Replace("/", "%2f")
$ReportLocation = $ReportLocation.Replace(" ", "+")
[string]$outputFile = $PSScriptRoot + '\Report.xlsx' #Save location for the file
#If the report has any parameters
[string]$ParamString = "";
$ParamString += "&param1=paramvalue"
$ParamString += "&param2=paramvalue"
[string]$URL = $ReportServer + "?" + $ReportLocation + "&rs:Command=Render&rs:Format=" + "EXCELOPENXML" + "&rs:ParameterLanguage=en-GB" + $ParamString
Write-Host $URL
$Req = [System.Net.WebRequest]::Create($URL);
$Req.Credentials = new-object System.Net.NetworkCredential($Username, $Password, $Domain)
$Req.Timeout = 30000;
$WebStream = $Req.GetResponse().GetResponseStream();
$MemStream = New-Object System.IO.MemoryStream
$WebStream.CopyTo($MemStream);
[long]$Len = $MemStream.Length;
[byte[]]$outBytes = [System.Byte[]]::CreateInstance([System.Byte], $Len)
$MemStream.Seek(0, [System.IO.SeekOrigin]::Begin);
$MemStream.Read($outBytes, 0, [int]$Len);
$WebStream.Close();
$MemStream.Close();
$MemStream.Dispose();
$Stream = New-Object System.IO.FileStream($outputFile), Create, Write
$Stream.Write($outBytes, 0, $outBytes.Length)
$Stream.Close()
Invoke-Item $outputFile

Related

Insert a new Mongo row using PowerShell

I have a PowerShell script that has been vexing me all day. I've finally gotten to the point where I can get a collection but I'm getting an error that I can't figure out.
function Get-MongoDBCollection {
Param(
$database,
$CollectionName,
$settings = $null, #[MongoDB.Driver.MongoCollectionSetting]
$returnType = [PSOBJECT]
)
$method = $database.GetType().GetMethod('GetCollection')
$gericMethod = $method.MakeGenericMethod($returnType)
$gericMethod.Invoke($database,[object[]]($CollectionName,$settings))
}
$dbName = "MyDatabaseName"
$collectionName = "MyCollectionName"
try {
add-type -path 'C:\Program Files\MongoDB\Drivers\System.Runtime.InteropServices.RuntimeInformation.4.0.0\lib\net45\System.Runtime.InteropServices.RuntimeInformation.dll'
Add-Type -Path "C:\Program Files\MongoDB\Drivers\MongoDB.Bson.2.6.0\lib\net45\MongoDB.Bson.dll"
add-type -path "C:\Program Files\MongoDB\Drivers\DnsClient.1.0.7\lib\net45\DnsClient.dll";
Add-Type -path "C:\Program Files\MongoDB\Drivers\MongoDB.Driver.Core.2.6.0\lib\net45\MongoDb.Driver.Core.dll"
Add-Type -Path "C:\Program Files\MongoDB\Drivers\MongoDB.Driver.2.6.0\lib\net45\MongoDB.Driver.dll"
}
catch {
$_;
$_.Exception.LoaderExceptions
}
$connectionString = "mongodb://localhost:27018";
$mongoClient = new-object MongoDb.Driver.MongoClient($connectionString);
$mongoDatabase = $mongoclient.GetDatabase($dbName)
$mongoDatabase.GetCollection($collectionname)
$collection = Get-MongoDBCollection $mongodatabase "SharePoint" -returnType ([MongoDB.Bson.BsonDocument]);
$datafile = Get-Content -Raw -Path "D:\datafiles\86fba866-77ed-4f40-4637-08d57d2e25b4.json" #`| ConvertFrom-Json
[MongoDB.Bson.BsonDocument] $doc = [MongoDB.Bson.BsonDocument]::Parse($datafile);
$x = $collection.InsertOne($doc)
The script takes the contents of a file, which contains a JSON string and converts it to BsonDocument and then tries to insert it. I'm getting the following error.
Argument types do not match
At line:1 char:1
+ $collection.InsertOneAsync($doc)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : OperationStopped: (:) [], ArgumentException
+ FullyQualifiedErrorId : System.ArgumentException
What am I doing wrong here?
This is a tough cookie! Basically, powershell doesn't support generics in a trivial way.. It supports them, but in a complex and hard to understand way!
I found this snippet in another stack overflow post days ago but can't find it at this moment. It was an unpleasant search/trek to begin with. The snippet from the original author who i wish i could give proper credit is as follows:
###
# Usage:
# $Collection = Get-MongoDBCollection $database 'collectionName'
# or
# $Collection = Get-MongoDBCollection $database 'collectionName' -returnType ([MongoDB.Bson.BsonDocument])
function Get-MongoDBCollection {
Param(
$database,
$CollectionName,
$settings = $null, #[MongoDB.Driver.MongoCollectionSetting]
$returnType = [PSOBJECT]
)
$method = $database.GetType().GetMethod('GetCollection')
$gericMethod = $method.MakeGenericMethod($returnType)
$gericMethod.Invoke($database,[object[]]($CollectionName,$settings))
}
And here's the full context usage I had below. I also have to load the DnsClient.dll dependencies myself. Throughout the process, per usual, the powershell module failed to provide helpful errors.
PS: This is amateur powershell, only used .ps because i needed to interface Office365! No promise of best practice!
#########
# Globals
##
$mongoDbDriverPath = "Z:\Work\mb-rule-watch\lib\net45"
$dbName = "mbRules"
$collectionName = "Mailboxes"
#########
# Load o365 credentials/modules
##
Import-Module MsOnline
if( ! $credential ){
Write-Host "Requesting Credentials - Use o365 Admin Account"
$credential = Get-Credential
Connect-MsolService -Credential $credential
}
# Prep remote session connection
if( ! $session ){
$session = New-PSSession `
-ConfigurationName Microsoft.Exchange `
-ConnectionUri https://outlook.office365.com/powershell-liveid/ `
-Credential $credential `
-Authentication Basic `
-AllowRedirection
# import commands from Microsoft Exchange Server shell
Import-PSSession $session
}
###########
# Functions
##
###
# Usage:
# $Collection = Get-MongoDBCollection $database 'collectionName'
# or
# $Collection = Get-MongoDBCollection $database 'collectionName' -returnType ([MongoDB.Bson.BsonDocument])
function Get-MongoDBCollection {
Param(
$database,
$CollectionName,
$settings = $null, #[MongoDB.Driver.MongoCollectionSetting]
$returnType = [PSOBJECT]
)
$method = $database.GetType().GetMethod('GetCollection')
$gericMethod = $method.MakeGenericMethod($returnType)
$gericMethod.Invoke($database,[object[]]($CollectionName,$settings))
}
###########
# MAIN
##
try
{
# Load mongo driver
Add-Type -Path "$($mongoDbDriverPath)\DnsClient.dll"
Add-Type -Path "$($mongoDbDriverPath)\MongoDB.Bson.dll"
Add-Type -Path "$($mongoDbDriverPath)\MongoDB.Driver.Core.dll"
Add-Type -Path "$($mongoDbDriverPath)\MongoDB.Driver.dll"
# Connect to mongo
$client = new-object -TypeName MongoDB.Driver.MongoClient -ArgumentList "mongodb://localhost"
# Get DB handle
[MongoDB.Driver.IMongoDatabase] $db = $client.GetDatabase( $dbName );
# Aquire Collection handle with brute force generic hacks Via a PS god on stackoverflow.
$collection = Get-MongoDBCollection $db $collectionName -returnType ([MongoDB.Bson.BsonDocument])
#foreach( $mbx in $( Get-Mailbox -ResultSize Unlimited -identity example_user_id ) ){
foreach( $mbx in $( Get-Mailbox -ResultSize Unlimited ) ){
$identityStr = $mbx.identity
$rules = Get-InboxRule -Mailbox $identityStr
# convert some huge ints (>Mongo Int64) to strings
foreach( $rule in $rules ){
$rule.RuleIdentity = "" + $rule.RuleIdentity + ""
}
# Json Stringify
$rules_json = ConvertTo-Json $rules
# If the mailbox had rules
if( $rules_json ){
write-host( "Inserting rules for: " + $identityStr )
# Cache results to FS this time.
echo $rules_json > var\rules\$identityStr.rules.json
try{
# Type convert/parse our json string
$document = new-object -TypeName MongoDB.Bson.BsonDocument
$document = [MongoDb.Bson.BsonDocument]::Parse( '{ "_username":"' + $identityStr + '","rules": ' + $rules_json + '}' );
# Insert the JSON document
$collection.InsertOne( $document )
} catch {
Write-Host "JSON parse or mongo insert failure"
foreach( $x IN $_.Exception ){
foreach( $msg IN $x ){
Write-Error $msg
}
}
}
}
}
}
catch
{
Write-Host "Script errors occured"
if( $_.Exception.LoaderExceptions ){
Write-Host "!!Dependency Loads Failed!!"
foreach( $msg IN $_.Exception.LoaderExceptions ){
Write-Error $msg
}
} else {
foreach( $x IN $_.Exception ){
foreach( $msg IN $x ){
Write-Error $msg
}
}
}
}

I'm trying to Write a Powershell script that will download all attachments, When i it Downloads the first 10 attachments only

$password = ConvertTo-SecureString "xxx" -AsPlainText -Force
$cred = New-Object System.Management.Automation.PSCredential "xxx#xxx.onmicrosoft.com",$password
$url = "https://outlook.office365.com/api/v1.0/me/messages"
$today = (Get-Date).AddDays(1).ToString("yyyy-MM-dd")
## Set date and query
$messageQuery = "" + $url + "?`$select=Id&`$filter=HasAttachments eq true and DateTimeReceived lt " + $today
$messages = Invoke-RestMethod $messageQuery -Credential $cred
## Loop through each results
foreach ($message in $messages.value)
{
# get attachments and save to file system
$query = $url + "/" + $message.Id + "/attachments"
$attachments = Invoke-RestMethod $query -Credential $cred
# in case of multiple attachments in email
foreach ($attachment in $attachments.value)
{
$attachment.Name
$path = "C:\path\" + $attachment.Name
$Content = [System.Convert]::FromBase64String($attachment.ContentBytes)
Set-Content -Path $path -Value $Content -Encoding Byte
}
}
When this is run only 10 attachments are downloaded then it stops.
I would like to figure out the reason why it stops at 10 so that i could go ahead and download an entire inbox.
I am having the same issue. I think it only handles X amount at a time based on attachment size. I delete mine out of the mailbox when I have successfully copied the attachment with these lines, since I do not need the email after I have harvested the attachments.
This (with the rest of my code) runs in Task Scheduler every 5 minutes, so if it doesn't catch all the mail in the first run, they will be processed, I guess 10 at a time in subsequent runs.
If ($DeleteEmail -eq "Y") {
$query2 = $url + "/" + $message.Id
Invoke-RestMethod $query2 -Credential $Credentials -Method Delete
}

Azure storage API: download entire folder / prefix / directory

I need to be able to download a folder with its contents from a blob storage account using the Azure Storage REST API only.
I have created a function (New-StorageAccountAthorisationHeader) that creates the (authentication) header that I can download a single file, but I cannot find any reference on how I might go about downloading the whole folder.
If I pass the folder as the $blob parameter, I get a BlobNotFound error.
The URL of the said folder is: https://mystorageaccount.blob.core.windows.net/acontainer/somefolder. The contents of "somefolder" looks like:
Folder1
FolderA
FileA.txt
FolderB
FileB.txt
FileC.txt
New-StorageAccountAthorisationHeader:
function New-StorageAccountAuthorizationHeader
{
[cmdletbinding()]
param
(
[string]$StorageAccountName,
[string]$Container,
[string]$Blob,
[string]$accesskey ,
[string]$ResourceUri,
[string]$xmsversion = "2017-04-17"
)
$xmsdate = Get-Date
$xmsdate = $xmsdate.ToUniversalTime()
$xmsdate = $xmsdate.toString('r')
function GetRestApiParameters
{
[cmdletbinding()]
param
(
[Parameter(Mandatory=$true)]
[string]$Uri
)
if($Uri.Contains("?"))
{
Write-Verbose "URI to extract REST parameters: $uri"
return ($Uri.Split("?")[1]).Split("&")
}
}
Write-Verbose "Generating string for signature encryption..."
$partUrl = "/$StorageAccountName/"
if($Container)
{
$partUrl = $partUrl + "$Container/"
}
if($Blob)
{
$parturl = $partUrl + "$Blob"
}
######Don't change the line count or indentation of the here-string#####
$hereString = #"
GET
x-ms-date:$xmsdate
x-ms-version:$xmsversion
$partUrl
"#
$hereString =$hereString -replace "$([char]13)$([char]10)","$([char]10)" #Change `r`n to just `n
$empty = $oSignature = New-Object System.Text.StringBuilder
$empty = $oSignature.Append($hereString)
Write-Verbose "Appending parameters from URI into authorisation string..."
$restParameters = GetRestApiParameters -Uri $ResourceUri -Verbose
if ($restParameters -ne $null)
{
foreach ($param in $restParameters)
{
$empty = $oSignature.Append("$([char]10)$($param.Replace('=',':'))")
}
}
#$oSignature.toString()
Write-Verbose "Encrypting string..."
$hmacsha = New-Object System.Security.Cryptography.HMACSHA256
$hmacsha.key = [Convert]::FromBase64String($accesskey)
$signature = $hmacsha.ComputeHash([Text.Encoding]::UTF8.GetBytes($oSignature.ToString()))
$signature = [Convert]::ToBase64String($signature)
Write-Verbose "Building header..."
$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$headers.Add("x-ms-version", $xmsversion)
$headers.Add("x-ms-date", $xmsdate)
$headers.Add("Authorization", "SharedKey " + $StorageAccountName + ":" + $signature)
#$headers.Add("x-ms-blob-type","BlockBlob")
#$headers.Add("Content-Type", "application\xml")
Write-Verbose ("Header: $($headers | Out-String)")
Return $headers
}
And I would call it:
$StorageAccountName = "mystorageaccount"
$container = "acontainer"
$blob = "somefile.txt"
$uriToDownloadBlobs = "https://" + $StorageAccountName + ".blob.core.windows.net/$container/$blob"
$header = $null
$header = New-StorageAccountAuthorizationHeader -StorageAccountName $StorageAccountName -ResourceUri $uriToDownloadBlobs -Verbose -Container $container -Blob $blob
$result = Invoke-WebRequest -Headers $header -Uri $uriToDownloadBlobs -OutFile C:\Temp\$blob -PassThru
$result
So this works, but as I said, I'm after any hints to help with downloading the whole folder.
It looks like this is not possible? Although I'd be interested to see how it's done with the likes of Azure Storage Explorer.
My solution was to zip the files up and then use the above to download the single ZIP file. A few extra lines of code to compress and extract at either end, but it was the quickest way at the time and it works well with VSTS tasks.

Pass List to Variable in URL String

I am trying to use a variable that loops through a list of words to be used in downloading password-protected files from a URL.
sites.txt:
BOS
HFD
LGA
NYC
PHI
WWD
Powershell Script:
$sites = Get-Content C:\Users\...\sites.txt
$time = (Get-Date).ToString("yyyyMMdd")
$Username = 'hello'
$Password = 'world'
$url = "http://my.website/" + $sites + "/some.csv"
$Path = "D:\...\...\" + $sites + "/some.csv"
$WebClient = New-Object System.Net.WebClient
$WebClient.Credentials = New-Object System.Net.Networkcredential($Username, $Password)
$WebClient.DownloadFile( $url, $path )
I am getting "unexpected token" errors so, I'm assuming that I am not using the $site variable correctly in the URL string.
$sites = Get-Content C:\Users\...\sites.txt
ForEach ($Site in $sites){
$url = "http://my.website/" + $Site + "/some.csv"
$Path = "D:\...\...\" + $Site + "\some.csv"
$Path
$url
}
This will pass your list into individual URL's
Edited to include change to '$Path' as well.

SSRS and powershell: Parameter not accepted

I use Powershell to run several reports on Microsoft SQL Report Services and to save the results to a Word doc. I have a script with functions that handle communications with the Report Server:
## File "qrap-functions.ps1"
function GetRSConnection($server, $instance)
{
$User = "xxxx"
$PWord = ConvertTo-SecureString -String "yyyy" -AsPlainText -Force
$c = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $User, $PWord
$reportServerURI = "http://" + $server + "/" + $instance + "/ReportExecution2005.asmx?WSDL"
$RS = New-WebServiceProxy -Class 'RS' -NameSpace 'RS' -Uri $reportServerURI -Credential $c
$RS.Url = $reportServerURI
return $RS
}
function GetReport($RS, $reportPath)
{
$reportPath = "/" + $reportPath
#$reportPath
$Report = $RS.GetType().GetMethod("LoadReport").Invoke($RS, #($reportPath, $null))
$parameters = #()
$RS.SetExecutionParameters($parameters, "nl-nl") > $null
return $report
}
function AddParameter($params, $name, $val)
{
$par = New-Object RS.ParameterValue
$par.Name = $name
$par.Value = $val
$params += $par
return ,$params
}
function GetReportInFormat($RS, $report, $params, $format, $saveas)
{
$deviceInfo = "<DeviceInfo><NoHeader>True</NoHeader></DeviceInfo>"
$extension = ""
$mimeType = ""
$encoding = ""
$warnings = $null
$streamIDs = $null
$RS.SetExecutionParameters($params, "nl-nl") > $null
$RenderOutput = $RS.Render($format,
$deviceInfo,
[ref] $extension,
[ref] $mimeType,
[ref] $encoding,
[ref] $warnings,
[ref] $streamIDs
)
$Stream = New-Object System.IO.FileStream($saveas), Create, Write
$Stream.Write($RenderOutput, 0, $RenderOutput.Length)
$Stream.Close()
}
Then, I have a script that executes a report containing the financial quarterly data. This script runs fine:
## File "qrap-financieel.ps1"
. "./qrap-functions.ps1"
$saveas = "e:\test\financieel.doc"
$RS = GetRSConnection -server "MSZRDWH" -instance "reportserver_acc"
$report = GetReport -RS $RS -reportPath "kwartaalrapportage/kwartaalrapportage financieel"
$params = #()
$kwartaal = "[Periode Maand].[Jaar Kwartaal].&[2015-2]"
$kptc = "[Kostenplaats].[Team code].&[2003]"
$params = AddParameter -params $params -name "PeriodeMaandJaarKwartaal" -val $kwartaal
$params = AddParameter -params $params -name "KostenplaatsTeamcode" -val $kptc
GetReportInformat -RS $RS -report $report -params $params -format "WORD" -saveas $saveas
The values for $kwartaal and $kptc are hard-coded here, but are parameters in the actual version of this script. Besides the financial quarterly, we have three other quarterly reports that need to be output by this script.
Two of these run fine, in the fourth I can't seem to get one of the parameters right. The script for that one is:
## File "qrap-zorglog.ps1"
. "./qrap-functions.ps1"
$RS = GetRSConnection -server "MSZRDWH" -instance "reportserver_acc"
$report = GetReport -RS $RS -reportPath "kwartaalrapportage/kwartaalrapportage zorglogistiek"
$s = "Urologie"
$saveas = "e:\test\ZL Urologie.doc"
$params = #()
$kwartaal = "[Periode Maand].[Jaar Kwartaal].&[2015-2]"
$params = AddParameter -params $params -name "HoofdspecialismeSpecialismeOms" -val "[Hoofdspecialisme].[Specialisme Oms].&[$s]"
$params = AddParameter -params $params -name "PeriodeMaandJaarKwartaal" -val $kwartaal
$params = AddParameter -params $params -name "WachttijdenSpecialismeSpecialisme" -val "[Wachttijden Specialisme].[Specialisme].&[$s]"
$params = AddParameter -params $params -name "SpecialisatieGroeperingSpecialisatieGroeperingOms" -val "[Specialistie Groepering].[Specialistie Groepering Oms].&[$s]"
$params = AddParameter -params $params -name "AanvragendSpecialismeSpecialismeOms" -val "[AanvragendSpecialisme].[Specialisme Oms].&[$s]"
GetReportInformat -RS $RS -report $report -params $params -format "WORD" -saveas $saveas
When I execute this script, I get this error:
Exception calling "Render" with "7" argument(s): "System.Web.Services.Protocols.SoapException: This report requires a
default or user-defined value for the report parameter 'HoofdspecialismeSpecialismeOms'. To run or subscribe to this
report, you must provide a parameter value. ---> Microsoft.ReportingServices.Diagnostics.Utilities.ReportParameterValueNot
SetException: This report requires a default or user-defined value for the report parameter
'HoofdspecialismeSpecialismeOms'. To run or subscribe to this report, you must provide a parameter value.
I clearly DO supply a value for 'HoofdspecialismeSpecialismeOms'; I've previously noticed that this error also is thrown when the parameter is not in the expected format. This format, since the
report filter is based on a hierarchy in an SSAS cube, looks like this: [hierarchy].[sub-level].&[member]. I've ensured that [Hoofdspecialisme].[Specialisme Oms].&[$s] is the correct format by
looking it up in the query that populates the prompt in SSRS. The report does display data when run through SSRS - and taking a parameter from the prompt.
I did notice that this parameter allows multiple selection. However, I don't believe this leads to the error because that is also true for AanvragendSpecialismeSpecialismeOms.
Any idea why this one parameter fails to be fed into the report when calling GetReportInformat?
Have you tried
function AddParameter($params, $name, $val)
{
$par = New-Object RS.ParameterValue
$par.Name = $name
$par.Value = $val
$params += $par
return ,$params
# ^Removing this comma?
}
As well as declaring the data types explicitly for your parameters?
function AddParameter([Array]$params, [String]$name, [String]$val)
{
$par = New-Object RS.ParameterValue
$par.Name = $name
$par.Value = $val
$params += $par
return ,$params
}
Also, with so many user-defined helper functions calling imported types that call methods and set properties to a report we can't see, it can get a little difficult to help troubleshoot in-depth for this specific report you're getting an error on. It looks like you've tried moving the line around in the order which sounds to me like you might have an issue with how that specific report parses the values you input through RS.ParameterValue so maybe take a look at if it accepts the string you set in -val for your AddParameter user defined function.
Edit:
From https://social.msdn.microsoft.com/Forums/sqlserver/en-US/e38b4a34-c780-43bb-8321-15f96d0938a9/exception-calling-render-systemwebservicesprotocolssoapexception-one-or-more-data-source?forum=sqlreportingservices
This error is generated when you are attempting to run a report in which one or more of the data sources are set to "prompt" credentials. This means we do not use your Windows credentials automatically, but rather you need to supply a different set of credentials which are used only for the data source.
Sounds like you might need to put aside the script and check if the report is different.
I've finally figured it out: The failing prompt had a multi-select enabled. And when filling in a multi-select, SSRS expects a list of values. When only given one string, the string is ignored and the parameter is assumed blank.
To feed it a list, we must do:
$multival = New-Object System.Collections.Specialized.StringCollection
$multival.Add("[Hoofdspecialisme].[Specialisme Oms].&[$s]")
[snip]
$params = AddParameter -params $params -name "HoofdspecialismeSpecialismeOms" -val $multival
Found the answer thanks to this question:
How to pass multiple value parameter to reporting services report via powershell