SSRS report using PowerShell Start-Job - powershell

I have a PowerShell GUI that is pulling some values from a SSRS report using an String array input. However, as this would freeze the GUI, I decided to use Start-Job to start a job that pulls the SSRS report while a ProgressBar keeps running in the GUI.
The SSRS report has only one input parameter. When I use Start-Job to Render the report using multiple values, I get the result of only the first record irrespective of the number of input values.
The same function works smoothly when called natively without Start-Job returning the records for all the input values.
This is the code:
$GetSSRSData = {
param([string[]]$InputArray)
$reportServerURI = "https://<SERVER>/ReportServer/ReportExecution2005.asmx?wsdl"
$RS = New-WebServiceProxy -Class 'RS' -NameSpace 'RS' -Uri $reportServerURI -UseDefaultCredential
$RS.Url = $reportServerURI
$deviceInfo = "<DeviceInfo><NoHeader>True</NoHeader></DeviceInfo>"
$extension = ""
$mimeType = ""
$encoding = ""
$warnings = $null
$streamIDs = $null
$reportPath = "/Folder/Report"
$Report = $RS.GetType().GetMethod("LoadReport").Invoke($RS, #($reportPath, $null))
# Report parameters are handled by creating an array of ParameterValue objects.
$parameters = #()
for($i = 0; $i -lt $InputArray.Count; $i++){
$parameters += New-Object RS.ParameterValue
$parameters[$i].Name = "ParameterName"
$parameters[$i].Value = "$($InputArray[$i])"
}
# 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
# Render the report to a byte array. The first argument is the report format.
$RenderOutput = $RS.Render('CSV',
$deviceInfo,
[ref] $extension,
[ref] $mimeType,
[ref] $encoding,
[ref] $warnings,
[ref] $streamIDs
)
$output = [System.Text.Encoding]::ASCII.GetString($RenderOutput)
return $output
}
$InputArray = #('XXXXXX', 'YYYYYY', 'ZZZZZZ', 'ABCDEF')
<#
# The below code works perfectly
$Data = GetSSRSData -InputArray $InputArray
ConvertFrom-Csv -InputObject $Data
#>
$job = Start-Job -ScriptBlock $GetSSRSData -ArgumentList $InputArray
do { [System.Windows.Forms.Application]::DoEvents() } until ($job.State -ne "Running")
$Data = Receive-Job -Job $job
Write-Host $Data # returns only the first record
When I change the bottom part as shown below, I am able to verify that the job ends after the first record is output.
$RenderOutput = $RS.Render('CSV',
$deviceInfo,
[ref] $extension,
[ref] $mimeType,
[ref] $encoding,
[ref] $warnings,
[ref] $streamIDs
)
Write-Output $RenderOutput
}
$InputArray = #('XXXXXX', 'YYYYYY', 'ZZZZZZ', 'ABCDEF')
$job = Start-Job -ScriptBlock $GetSSRSData -ArgumentList $InputArray
do {[System.Text.Encoding]::ASCII.GetString($job.ChildJobs[0].Output)} until ($job.State -ne "Running")
I also tried adding a sleep function of 5 seconds after the Render function, but it did not make any difference.
Please note that repeatedly calling the Start-Job for each input is not an option as each function call is costing a lot of time and hence the report needs to be pulled in a single call.
Why is the Render function behaving differently when started as a job? Is the function ending prematurely before it can render the other records as well?
Is there any other way such as a Runspace or a Start-ThreadJob that can solve this problem?
Ref: https://stackoverflow.com/a/63253699/4137016

Answer is here: ArgumentList parameter in Invoke-Command don't send all array
Probably better answer here: How do I pass an array as a parameter to another script?
Change -ArgumentList $InputArray to -ArgumentList (,$InputArray)
$InputArray = #('XXXXXX', 'YYYYYY', 'ZZZZZZ', 'ABCDEF')
$job = Start-Job -ScriptBlock $GetSSRSData -ArgumentList (,$InputArray)

Related

Pass command line arguments on to to "self" but with Start-Job

At [Asynchronous start][1] I had a question about starting a power-shell script asynchronously which creates a form. As answered in that question this can be solved using start-job
eg
Start-Job -ScriptBlock { test NW -NoWarning -Paranoia:2 }
So I have tried to write the Test.PS1 script routine so it re-calls itself with "Start-Job Test -NoSpawn" The switch nospawn then means it runs without a second call. I have tested this with the example code the above line now has to be and it works
Start-Job -ScriptBlock { test NW -NoSpawn -NoWarning -Paranoia:2 }
However I'm struggling to get the parameters from the original command line to passthrough to the job
I have tried creating a string in the correct format , an array , list the arguments manually , I either get repeated arguments being passed or all of the string ending up in the first Parameter $ComputerList -
A summary of the parameters and the attempts are
Param ([string]$ComputerList = 'status\edi.csv',[switch]$NoSpawn,[switch]$NoWarning,[switch]$Debug,[INT]$Paranoia=6)
...... <Snip>
Start-Job -ScriptBlock { test $ComputerList -NoSpawn -NoWarning:$NoWarning -Paranoia:$Paranoia }
Doesn't work due to scope - also switches are wrong way to do this
Start-Job -ScriptBlock { test -NoSpawn $Args } -argumentlist $ComputerList
Insufficent arguments but works - But I think One Argument is possible ?
Start-Job -ScriptBlock { test $Args -NoSpawn } -argumentlist #("-NoWarning:$NoWarning","-ComputerList:$ComputerList","-Paranoia:$Paranoia")
Everything ends up in $ComputerList
Start-Job -ScriptBlock { test $Args -NoSpawn } -argumentlist "-NoWarning:$NoWarning -ComputerList:$ComputerList -Paranoia:$Paranoia"
Everything ends up in $ComputerList
Full code follows
Param ([string]$ComputerList = 'status\edi.csv',[switch]$NoSpawn,[switch]$NoWarning,[switch]$Debug,[INT]$Paranoia=6)
$Log_Paranoia=$Paranoia
If ($Debug) { $debugPreference="Continue"} #enable debug messages if -debug is specified
If ($NoWarning) { $WarningPreference="SilentlyContinue"} #turn off warning messages
function Write-Paranoid($Level, $message) {
$CS=Get-PSCallStack
$Caller = $CS[1]
$Module = "$($Caller.FunctionName)[$($Caller.ScriptLineNumber)]"
$Diff=$level - $Log_Paranoia
$MSG= "$Module($($Level),$($Log_Paranoia)):$message"
if ($level - $Log_Paranoia -le 0 ) {
Write-host $MSG
}
if($Error.Count -gt 0 ) {
$MSG= "$Module($Level)ERROR:$Error[0]"
Write-Error $MSG
}
$error.clear()
}
Function AddStatusBar($form , $Txt) {
Write-Paranoid 10 "Enter"
$statusBar = New-Object System.Windows.Forms.StatusBar
$statusBar.DataBindings.DefaultDataSourceUpdateMode = 0
$statusBar.TabIndex = 4
$statusBar.Size = SDS 428 22
$statusBar.Location = SDP 0 337
$statusBar.Text = $Txt
$form.Controls.Add($statusBar)
$statusBar
Write-Paranoid 10 "Exit"
}
Function Create-Form ($Title)
{
Write-Paranoid 10 "Enter"
$form1 = New-Object System.Windows.Forms.Form
$form1.Text = $Title
$form1.DataBindings.DefaultDataSourceUpdateMode = 0
$form1.ClientSize = SDS 890 359
$form1.StartPosition = 0
$form1.BackColor = [System.Drawing.Color]::FromArgb(255,185,209,234)
$form1
Write-Paranoid 10 "Exit"
}
Function GenerateTestForm
{
Write-Paranoid 10 "Enter"
[reflection.assembly]::loadwithpartialname("System.Drawing") | Out-Null
[reflection.assembly]::loadwithpartialname("System.Windows.Forms") | Out-Null
$Form1 = Create-Form "Test Form"
$Alist = Get-CommandLine
$StatusBar = AddStatusBar $form1 $AList
$form1.ShowDialog() | Out-Null # Suspends calller
Write-Paranoid 10 "Exit"
}
if ($NoSpawn )
{
Write-Paranoid 3 " NoSpawn "
Write-Paranoid 5 "Call GenerateForm"
if ($Test) {
GenerateTestForm
} else {
GenerateTestForm
}
} else {
Write-Paranoid 3 "NOT NoSpawn restarting as job"
# note that test.ps1 is in the path so it will restart this script
# Start-Job -ScriptBlock { test $ComputerList -NoSpawn -NoWarning:$NoWarning -Paranoia:$Paranoia } #Wrong scope
# Start-Job -ScriptBlock { test -NoSpawn $Args } -argumentlist $ComputerList # Insufficent aruments but works - ONLY One Argument possible -
# Start-Job -ScriptBlock { test $Args -NoSpawn } -argumentlist #("-NoWarning:$NoWarning","-ComputerList:$ComputerList","-Paranoia:$Paranoia") # Everything ends up in $ComputerList
# Start-Job -ScriptBlock { test $Args -NoSpawn } -argumentlist "-NoWarning:$NoWarning -ComputerList:$ComputerList -Paranoia:$Paranoia" # Everything ends up in $ComputerList
}
Your problem can be reduced to this:
How can I re-invoke the script at hand as a background job, passing all original arguments (parameter values, including default parameter values) through?
A simplified example:
param (
[string] $ComputerList = 'status\edi.csv',
[switch] $NoSpawn,
[switch] $NoWarning,
[switch] $Debug,
[int] $Paranoia=6
)
if ($NoSpawn) { # already running as a background job.
"I'm now running in the background with the following arguments:"
$PSBoundParameters
} else { # must re-invoke via a background job
# Add *default* parameter values, if necessary, given that
# they're *not* reflected in $PSBoundParameters.
foreach ($paramName in $MyInvocation.MyCommand.Parameters.Keys) {
if (-not $PSBoundParameters.ContainsKey($paramName)) {
$defaultValue = Get-Variable -Scope Local -ValueOnly $paramName
if (-not ($null -eq $defaultValue -or ($defaultValue -is [switch] -and -not $defaultValue))) {
$PSBoundParameters[$paramName] = $defaultValue
}
}
}
# Start a background job that reinvokes this script with the original
# arguments / default values.
Start-Job {
$argsHash = $using:PSBoundParameters
& $using:PSCommandPath -NoSpawn #argsHash
} |
Receive-Job -Wait -AutoRemoveJob
}
Note:
For demonstration purposes, the initial call waits for the re-invocation via a background job to finish, using Receive-Job -Wait -AutoRemoveJob
In your real code, you can simply discard Start-Job's output (a job-information object) with $null = Start-Job { ... }, and then rely on the job getting cleaned up when the caller's session as a whole exits.
The extra code needed to propagate parameter default values is somewhat cumbersome, but necessary, given that the automatic $PSBoundParameters variable does not reflect default values.
GitHub issue #3285 discusses this limitation, and suggests a potential future solution.

Cannot bind argument to parameter 'InputObject' because it is null

I have a powershell script that measures download time on some pages, however I get the error above, I am unsure what I am doing wrong
error is
Cannot bind argument to parameter 'InputObject' because it is null.
function ResponseTime($CommonName,$URL, $environment)
{
$Times = 5
$i = 0
$TotalResponseTime = 0
Write-HOst $URL
While ($i -lt $Times) {
$Request = New-Object System.Net.WebClient
$Request.UseDefaultCredentials = $true
$Start = Get-Date
Write-HOst $URL
$PageRequest = $Request.DownloadString($URL)
$TimeTaken = ((Get-Date) - $Start).TotalMilliseconds
$Request.Dispose()
$i ++
$TotalResponseTime += $TimeTaken
}
$AverageResponseTime = $TotalResponseTime / $i
Write-Host Request to $CommonName took $AverageResponseTime ms in average -ForegroundColor Green
$details = #{
Date = get-date
AverageResponseTime = $AverageResponseTime
ResponseTime = $Destination
Environment = $environment
}
$results += New-Object PSObject -Property $details
$random = Get-Random -minimum 1 -maximum 30
Start-Sleep -s $random
}
#PRODUCTION
ResponseTime -commonname 'app homepage' -URL 'https://url1' -environment 'PRODUCTION'
ResponseTime -commonname 'department homepage' -URL 'https://url2' -environment 'PRODUCTION'
$results | export-csv -Path c:\so.csv -NoTypeInformation
Reviewing your last edit, it seems that $results simply returns $null (As your error says)
The only line setting $results is $results += New-Object PSObject -Property $details
It is not in the scope of your Export-CSV call and - even if it would, $results could be empty, if this line is not called.
You should IMHO set it to e.g. an ArrayList like follows:
$results = New-Object -TypeName System.Collections.ArrayList
And add items to it via
$times = ResponseTime -commonname '' #etc
$results.Add($times) | Out-Null
This gives you an ArrayList - even if there are no items in it - which can easily be transformed to CSV and other formats.
#Clijsters has given the correct answer; i.e. the issue being the scope of your $results variable.
This answer just provides a bit of a code review to help you with other bits going forwards...
function Get-ResponseTime {
[CmdletBinding()]
param (
[Parameter(Mandatory = $true)]
[string]$CommonName
,
[Parameter(Mandatory = $true)]
[string]$URL
,
[Parameter(Mandatory = $true)]
[string]$Environment
,
[Parameter(Mandatory = $false)]
[int]$Times = 5
)
[System.Int64]$TotalResponseTime = 0
[System.Diagnostics.Stopwatch]$stopwatch = New-Object 'System.Diagnostics.Stopwatch'
Write-Verbose "Processing URL: $URL"
1..$times | foreach-object {
[System.Net.WebClient]$Request = New-Object 'System.Net.WebClient'
$Request.UseDefaultCredentials = $true
Write-Verboset "Call $_ to URL: $URL"
$stopwatch.Restart()
$PageRequest = $Request.DownloadString($URL)
$stopwatch.Stop()
$TimeTaken = $stopwatch.Elapsed.TotalMilliseconds
$Request.Dispose()
$TotalResponseTime += $TimeTaken
}
$AverageResponseTime = $TotalResponseTime / $Times
Write-Verbose "Request to $CommonName took $AverageResponseTime ms on average"
$details = #{
Date = get-date
AverageResponseTime = $AverageResponseTime
#ResponseTime = $Destination #this is not declared anywhere / don't know what this field's for
Environment = $environment
}
Write-Output (New-Object 'PSObject' -Property $details)
#do you really want a delay here? Doesn't make much sense... may make sense to include a delay in the above loop; i.e. to stagger your tests?
#$random = Get-Random -minimum 1 -maximum 30
#Start-Sleep -s $random
}
#PRODUCTION
[PSObject[]]$results = #(
(Get-ResponseTime -commonname 'app homepage' -URL 'https://url1' -environment 'PRODUCTION' -Verbose)
,(Get-ResponseTime -commonname 'department homepage' -URL 'https://url2' -environment 'PRODUCTION' -Verbose)
)
$results | Export-Csv -LiteralPath 'c:\so.csv' -NoTypeInformation
Use verb-noun function names (e.g. Get-Item). What is the naming convention for Powershell functions with regard to upper/lower case usage?
Use "Cmdlets" (Advanced Functions) instead of (Basic) Functions; they're basically the same thing, only tagged with [Cmdletbinding()]. The reason for this you get support for functionality such as verbose output. http://www.lazywinadmin.com/2015/03/standard-and-advanced-powershell.html
Use a stopwatch to time processes (you could also use measure-command; but any output would be suppressed / consumed by the measure-command function). Timing a command's execution in PowerShell
Have your cmdlet output its values to the pipeline via Write-Output (or you can leave off the function name; any output caused by placing a variable with nothing to process it will be fed to the pipeline; i.e. write-object $a is the same as a line solely consisting of $a).
Capture the output into your $results variable outside of the function, and handle the results there.

Expand variable with scriptblock inside variable in a loop with runspaces

$RunspaceCollection = #()
$RunspacePool = [RunspaceFactory]::CreateRunspacePool(1,5)
$RunspacePool.Open()
$code = #({'somecode'},{'someothercode'})
Foreach ($test in $case) {
$finalcode= {
Invoke-Command -ScriptBlock [scriptblock]::create($code[$test])
}.GetNewClosure()
$Powershell = [PowerShell]::Create().AddScript($finalcode)
$Powershell.RunspacePool = $RunspacePool
[Collections.Arraylist]$RunspaceCollection += New-Object -TypeName PSObject -Property #{
Runspace = $PowerShell.BeginInvoke()
PowerShell = $PowerShell
}}
The finalcode variable doesn't expand when the GetNewClosure() happens, so $code[$test] gets into the runspace instead of actual code and I can't get my desired results. Any advice?
Using the method from the answer I'm getting this in the runspace, but it doesn't execute properly. I can confirm that my command is loaded into runspace (at least while in debugger inside runspace I can execute it without dot sourcing)
[System.Management.Automation.PSSerializer]::Deserialize('<ObjsVersion="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04">
<SBK> My-Command -Par1 "egweg" -Par2 "qwrqwr" -Par3 "wegweg"</SBK>
</Objs>')
This is what I see in debugger in runspace
Stopped at: $a = Invoke-Command -ScriptBlock { $([System.Management.Automation.PSSerializer]::Deserialize('<Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04">
[DBG]: [Process:8064]: [Runspace12]: PS C:\git\infrastructure_samples>>
Stopped at: $a = Invoke-Command -ScriptBlock { $([System.Management.Automation.PSSerializer]::Deserialize('<Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04">
[DBG]: [Process:8064]: [Runspace12]: PS C:\git\infrastructure_samples>> s
Stopped at: </Objs>')) }
The problem with your code is that AddScript method of PowerShell class is expecting a string, not ScriptBlock. And any closure will be lost when you convert ScriptBlock to string. To solve this, you can pass argument to script with AddArgument method:
$RunspaceCollection = New-Object System.Collections.Generic.List[Object]
$RunspacePool = [RunspaceFactory]::CreateRunspacePool(1,5)
$RunspacePool.Open()
$code = #({'somecode'},{'someothercode'})
$finalcode= {
param($Argument)
Invoke-Command -ScriptBlock ([scriptblock]::create($Argument))
}
Foreach ($test in $case) {
$Powershell = [PowerShell]::Create().AddScript($finalcode).AddArgument($code[$test])
$Powershell.RunspacePool = $RunspacePool
$RunspaceCollection.Add((New-Object -TypeName PSObject -Property #{
Runspace = $PowerShell.BeginInvoke()
PowerShell = $PowerShell
}))
}
I'm not sure if there's a better way off the top of my head, but you can replace the variables yourself with serialized versions of the same.
You can't use $Using: in this case, but I wrote a function that replaces all $Using: variables manually.
My use case was with DSC, but it would work in this case as well. It allows you to still write your script blocks as scriptblocks (not as strings), and supports variables with complex types.
Here's the code from my blog (also available as a GitHub gist):
function Replace-Using {
[CmdletBinding(DefaultParameterSetName = 'AsString')]
[OutputType([String], ParameterSetName = 'AsString')]
[OutputType([ScriptBlock], ParameterSetName = 'AsScriptBlock')]
param(
[Parameter(
Mandatory,
ValueFromPipeline
)]
[String]
$Code ,
[Parameter(
Mandatory,
ParameterSetName = 'AsScriptBlock'
)]
[Switch]
$AsScriptBlock
)
Process {
$cb = {
$m = $args[0]
$ser = [System.Management.Automation.PSSerializer]::Serialize((Get-Variable -Name $m.Groups['var'] -ValueOnly))
"`$([System.Management.Automation.PSSerializer]::Deserialize('{0}'))" -f $ser
}
$newCode = [RegEx]::Replace($code, '\$Using:(?<var>\w+)', $cb, [System.Text.RegularExpressions.RegexOptions]::IgnoreCase)
if ($AsScriptBlock.IsPresent) {
[ScriptBlock]::Create($newCode)
} else {
$newCode
}
}
}
A better way for me to do this replacement would probably be to use the AST instead of string replacement, but.. effort.
Your Use Case
$finalcode= {
Invoke-Command -ScriptBlock [scriptblock]::create($Using:code[$Using:test])
} | Replace-Using
For better results you might assign a variable first and then just insert that:
$value = [scriptblock]::Create($code[$test])
$finalcode= {
Invoke-Command -ScriptBlock $Using:value
} | Replace-Using

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

Set Time limit for Get-process

Long story short, we are experiencing issues with some of our servers that cause crippling effects on them and I am looking for a way to monitor them, now I have a script that will check the RDP port to make sure that it is open and I am thinking that I want to use get-service and then I will return if it pulled any data or not.
Here is the issue I don't know how to limit the time it will wait for a response before returning false.
[bool](Get-process -ComputerName MYSERVER)
Although I like Ansgars answer with a time-limited job, I think a separate Runspace and async invocation fits this task better.
The major difference here being that a Runspace reuses the in-process thread pool, whereas the PSJob method launches a new process, with the overhead that that entails, such as OS/kernel resources spawning and managing a child process, serializing and deserializing data etc.
Something like this:
function Timeout-Statement {
param(
[scriptblock[]]$ScriptBlock,
[object[]]$ArgumentList,
[int]$Timeout
)
$Runspace = [runspacefactory]::CreateRunspace()
$Runspace.Open()
$PS = [powershell]::Create()
$PS.Runspace = $Runspace
$PS = $PS.AddScript($ScriptBlock)
foreach($Arg in $ArgumentList){
$PS = $PS.AddArgument($Arg)
}
$IAR = $PS.BeginInvoke()
if($IAR.AsyncWaitHandle.WaitOne($Timeout)){
$PS.EndInvoke($IAR)
}
return $false
}
Then use that to do:
$ScriptBlock = {
param($ComputerName)
Get-Process #PSBoundParameters
}
$Timeout = 2500 # 2 and a half seconds (2500 milliseconds)
Timeout-Statement $ScriptBlock -ArgumentList "mycomputer.fqdn" -Timeout $Timeout
You could run your check as a background job:
$sb = { Get-Process -ComputerName $args[0] }
$end = (Get-Date).AddSeconds(5)
$job = Start-Job -ScriptBlock $sb -ArgumentList 'MYSERVER'
do {
Start-Sleep 100
$finished = (Get-Job -Id $job.Id).State -eq 'Completed'
} until ($finished -or (Get-Date) -gt $end)
if (-not $finished) {
Stop-Job -Id $job.Id
}
Receive-Job $job.Id
Remove-Job $job.Id
This is a known issue: https://connect.microsoft.com/PowerShell/feedback/details/645165/add-timeout-parameter-to-get-wmiobject
There is a workaround provided Here : https://connect.microsoft.com/PowerShell/feedback/details/645165/add-timeout-parameter-to-get-wmiobject
Function Get-WmiCustom([string]$computername,[string]$namespace,[string]$class,[int]$timeout=15)
{
$ConnectionOptions = new-object System.Management.ConnectionOptions
$EnumerationOptions = new-object System.Management.EnumerationOptions
$timeoutseconds = new-timespan -seconds $timeout
$EnumerationOptions.set_timeout($timeoutseconds)
$assembledpath = "\\" + $computername + "\" + $namespace
#write-host $assembledpath -foregroundcolor yellow
$Scope = new-object System.Management.ManagementScope $assembledpath, $ConnectionOptions
$Scope.Connect()
$querystring = "SELECT * FROM " + $class
#write-host $querystring
$query = new-object System.Management.ObjectQuery $querystring
$searcher = new-object System.Management.ManagementObjectSearcher
$searcher.set_options($EnumerationOptions)
$searcher.Query = $querystring
$searcher.Scope = $Scope
trap { $_ } $result = $searcher.get()
return $result
}
You can call the function like this:
get-wmicustom -class Win32_Process -namespace "root\cimv2" -computername MYSERVER –timeout 1