diff --git a/README.md b/README.md index 434d44c5..c3823a83 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,14 @@ [![Build status](https://ci.appveyor.com/api/projects/status/github/microsoft/reportingservicestools?branch=master&svg=true)](https://ci.appveyor.com/project/jtarquino/reportingservicestools) [![ReportingServicesTools](https://img.shields.io/powershellgallery/v/ReportingServicesTools.svg?style=flat-square&label=ReportingServicesTools)](https://www.powershellgallery.com/packages/ReportingServicesTools/) # Reporting Services PowerShell -SQL Server Reporting Services PowerShell utilities +SQL Server Reporting Services PowerShell utilities ## Synopsis -This project contains PowerShell scripts that allows you to perform various operations with SQL Server Reporting Services and Power BI Report Server. In order to use the scripts included in this project successfully, please download/clone the entire project as there are dependencies between the scripts. +This project contains PowerShell scripts that allow you to perform various operations with SQL Server Reporting Services and Power BI Report Server. In order to use the scripts included in this project successfully, please download/clone the entire project as there are dependencies between the scripts. -All of our scripts were written with the assumption that you will be executing them against SQL Server 2016 Reporting Services default instance (i.e. mssqlserver). However, we understand this may not be the case for you. So for each script, you will see that we have provided a way for you to specify the name and/or version of your SQL Server Reporting Services or Power BI Report Server instance name. Sometimes the version of your SQL Server instance is also required. If you do not provide one, we will assume that you want to execute this against the default instance. +All of our scripts were written with the assumption that you will be executing them against Power BI Report Server (PBIRS). However, we understand this may not be the case for you. So for each script, you will see that we have provided a way for you to specify the SQL Server Reporting Services (SSRS) version instead of PBIRS. Sometimes the instance name is also required. If you do not provide one, we will assume that you want to execute this against the default instance. + +Note: SSRS 2017 and later, as well as PBIRS do not support changing the instance names from 'SSRS' and 'PBIRS' respectively. ## PowerShell Version Please ensure you're running PowerShell version 3.0+ @@ -26,31 +28,41 @@ or ```powershell Install-Module -Name ReportingServicesTools ``` - +Note: Using Install-Module is the preferred installation method for most users. ## List of commands -The following is a list of commands which are available for you to use once you follow the steps in Installation +The following is a list of commands which are available for you to use once you follow the steps in the Installation |Command|Description| |-------|-----------| |Backup-RsEncryptionKey|This command backs up the encryption key used by SQL Server Reporting Services to protect sensitive content.| -|Connect-RsReportServer|Connects to Reporting Services and sets default connection information| +|Connect-RsReportServer|This command connects to Reporting Services and sets default connection information.| |Copy-RsSubscription|This command adds a retrieved subscription to an existing report. For use with Get-RsSubscription.| |Export-RsSubscriptionXml|This command exports a collection of subscriptions to an XML file on disk.| |Get-RsFolderContent|This command lists all catalog items under a folder.| -|Get-RsDataSource|This command lists information about data source located at the specified path.| +|Get-RsDataSource|This command lists information about the data source located at the specified path.| +|Get-RsDeploymentConfig|This command retrieves a list of deployment configurations from a Reporting Services project file.| |Get-RsItemReference|This command gets the item references of a report or a dataset.| -|Get-RsItemDataSource|This command fetches embedded data sources associated to a report.| -|Get-RsCatalogItemRole|This command retrieves access on catalog items for users or groups.| -|Get-RsRestItemDataSource|This command fetches embedded data sources associated to a Paginated report or a Power BI report using the REST Endpoint.| +|Get-RsItemDataSource|This command fetches embedded data sources associated with a report.| +|Get-RsCatalogItemRole|This command retrieves access to catalog items for users or groups.| +|Get-RsRestCacheRefreshPlan|This function fetches a CacheRefreshPlan from a Power BI report.| +|Get-RsRestCacheRefreshPlanHistory|This function fetches the history of CacheRefreshPlan(s) from a Power BI report.| +|Get-RsRestDataSource|This function fetches the a datasource item from a folder.| +|Get-RsRestFolderContent|This function fetches data sources related to a catalog item from the Report Server.| +|Get-RsRestItem|This function fetches a catalog item from the Report Server using the REST API.| +|Get-RsRestItemAccessPolicy|This function retrieves access policies to SQL Server Reporting Services Instance or Power BI Report Server Instance from users/groups.| +|Get-RsRestItemDataModelParameters|This function fetches the Data Model Parameters related to a Catalog Item report from the Report Server. This is currently only applicable to Power BI Reports and only from ReportServer October/2020 or higher.| +|Get-RsRestItemDataSource|This command fetches embedded data sources associated with a Paginated report or a Power BI report using the REST Endpoint.| |Get-RsSubscription|This command retrieves information about subscriptions for a report.| +|Grant-RsRestItemAccessPolicy|This function grants access policies on the SQL Server Reporting Services Instance or Power BI Report Server Instance to the specified user/group, using the REST Endpoint.| |Grant-RsSystemRole|This command grants access to SQL Server Reporting Services to users or groups. Alias: Grant-AccessToRs| |Grant-RsCatalogItemRole|This script grants access to catalog items to users or groups. Alias: Grant-AccessOnCatalogItem| |Import-RsSubscriptionXml|This command imports a collection of subscriptions from an XML file on disk, typically created via Export-RsSubscriptionXml.| -|Initialize-Rs|This command initializes Report Server post installation. The database MUST be configured and URLs MUST be reserved prior to running this command.| +|Initialize-Rs|This command initializes Report Server post-installation. The database MUST be configured and URLs MUST be reserved prior to running this command.| |New-RsConfigurationSettingObject|This command creates a new RSConfigurationSettingObject which is used to interact with the WMI Provider.| -|New-RsDataSource|This command creates/overwrites data source to the specified path.| +|New-RsDataSource|This command creates/overwrites the data source to the specified path.| |New-RsFolder|This command creates a new folder in the specified path.| +|New-RsRestCacheRefreshPlan|This function creates a new CacheRefreshPlan for the specified Power BI Report.| |New-RsRestCredentialsByUserObject|This command creates a CredentialsByUser object to be used by Set-RsRestItemDataSource command.| |New-RsRestCredentialsInServerObject|This command creates a CredentialsInServer object to be used by Set-RsRestItemDataSource command.| |New-RsRestFolder|This command creates a new folder in the specified path using the REST Endpoint.| @@ -59,16 +71,18 @@ The following is a list of commands which are available for you to use once you |New-RsScheduleXml|This command creates an XML string definition of a subscription schedule. For use with the -Schedule parameter or New-RsSubscription.| |New-RsWebServiceProxy|This command creates a new Web Service Proxy which is used to interact with the SOAP Endpoint.| |Out-RsCatalogItem|This command downloads a catalog item.| -|Out-RsFolderContent|This command downloads all catalog items in folder.| +|Out-RsFolderContent|This command downloads all catalog items in a folder.| |Out-RsRestFolderContent|This command downloads all catalog items under a folder using the REST Endpoint.| |Out-RsRestCatalogItem|This command downloads a catalog item using the REST Endpoint.| +|Publish-RsProject|This command deploys a Reporting Services project to a Power BI Report Server.| |Register-PowerBI|This command registers Power BI information with SQL Server Reporting Services. Alias: Register-RSPowerBI| -|Remove-RsCatalogItem|This command removes catalog item located at the specified path.| +|Remove-RsCatalogItem|This command removes the catalog item located at the specified path.| |Remove-RsRestCatalogItem|This command removes catalog item located at the specified path using the REST Endpoint.| |Remove-RsRestFolder|This command removes folder located at the specified path using the REST Endpoint.| |Remove-RSSubscription|This command removes a subscription associated with a report.| -|Restore-RsEncryptionKey|This command restores encryption key on to the SQL Server Reporting Services.| -|Revoke-RsCatalogItemAccess|This command revokes access on catalog item from users or groups. Alias: Revoke-AccessOnCatalogItem| +|Restore-RsEncryptionKey|This command restores the encryption key to the SQL Server Reporting Services.| +|Revoke-RsCatalogItemAccess|This command revokes access to a catalog item from users or groups. Alias: Revoke-AccessOnCatalogItem| +|Revoke-RsRestItemAccessPolicy|This function revokes access to catalog items from users/groups, using the REST Endpoint.| |Revoke-RsSystemAccess|This command revokes access on SQL Server Reporting Services from users or groups. Alias: Revoke-AccessToRs| |Set-RsDatabase|This command configures the database used by SQL Server Reporting Services.| |Set-RsDatabaseCredentials|This command configures the credentials to use when connecting to the database used by SQL Server Reporting Services.| @@ -76,20 +90,24 @@ The following is a list of commands which are available for you to use once you |Set-RsDataSource|This command updates information associated to a data source. Alias: Set-RsDataSourceReference| |Set-RsDataSourcePassword|This command sets the password associated with a data source.| |Set-RsEmailSettings|This command configures the SQL Server Reporting Services email settings to use basic authentication. Alias: Set-RsEmailSettingsAsBasicAuth, Set-RsEmailSettingsAsNoAuth, Set-RsEmailSettingsAsNTLMAuth| -|Set-RsItemDataSource|This command updates embedded data source associated to a report.| -|Set-RsRestItemDataSource|This command updates embedded data sources associated to a Paginated report or a Power BI Report using the REST endpoint.| +|Set-RsItemDataSource|This command updates embedded data sources associated with a report.| +|Set-RsDataSourceReference|This command overrides the reference of a report or dataset to a shared data source.| +|Set-RsRestItemDataModelParameters|This function updates data sources related to a catalog item from the Report Server.| +|Set-RsRestItemDataSource|This command updates embedded data sources associated with a Paginated report or a Power BI Report using the REST endpoint.| |Set-RsSharedDataSource|This command links a report or a dataset to a data source.| |Set-RsUrlReservation|This command configures the SQL Server Reporting Services URLs.| |Set-PbiRsUrlReservation|This command configures the Power BI Report Server URLs.| -|Set-RsSubscription|This command updates existing subscriptions piped from Get-RsSubscription| -|Write-RsCatalogItem|This command uploads a report, a dataset or a data source using the SOAP Endpoint..| -|Write-RsFolderContent|This uploads all reports, datasets and data sources in a folder.| -|Write-RsRestCatalogItem|This command uploads a report, a dataset or a mobile report using the REST Endpoint.| -|Write-RsRestFolderContent|This uploads all reports, datasets, data sources, mobile reports and Power BI reports in a folder using the REST Endpoint.| +|Set-RsSubscription|This command updates existing subscriptions piped from Get-RsSubscription.| +|Start-RsRestCacheRefreshPlan|This function fetches the CacheRefreshPlan of a report from the Report Server and refreshes them using the REST API. Alternatively, when a report has multiple CacheRefreshPlans you can specify which CacheRefreshPlan to refresh by passing the Id of the CacheRefreshPlan to the -Id parameter.| +|Test-RsRestItemDataSource|This function fetches the DataSources from a Paginated or Power BI report and tests them to see if the connection can be made.| +|Write-RsCatalogItem|This command uploads a report, a dataset, or a data source using the SOAP Endpoint.| +|Write-RsFolderContent|This uploads all reports, datasets, and data sources in a folder.| +|Write-RsRestCatalogItem|This command uploads a report, a dataset, or a mobile report using the REST Endpoint.| +|Write-RsRestFolderContent|This uploads all reports, datasets, data sources, mobile reports, and Power BI reports in a folder using the REST Endpoint.| ## SQL Server Versions -Some of the commands listed above allow you to optionally specify the version of your SQL Server Reporting Services instance. The following is a list of versions associated to each SQL Server Reporting Services release. +Some of the commands listed above allow you to optionally specify the version of your SQL Server Reporting Services instance. The following is a list of versions associated with each SQL Server Reporting Services release. |SQL Server Release|Version| |------------------|-------| @@ -98,20 +116,22 @@ Some of the commands listed above allow you to optionally specify the version of |SQL Server 2016|13| |SQL Server 2017|14| |SQL Server 2019|15| +|SQL Server 2022|16| +|Power BI Report Server|15| ## Motivation -The motivation behind this project was to help users perform SQL Server Reporting Services operations via the command line. +The motivation behind this project was to help users perform SQL Server Reporting Services operations via the command line. ## API Reference -All of the APIs used by this project are publicly available. There are 2 types of APIs used in this repository: SOAP and WMI. You can find more details about the SOAP API at https://msdn.microsoft.com/en-us/library/ms154052.aspx and the WMI API at https://msdn.microsoft.com/en-us/library/ms152836.aspx. In general, you will use SOAP API for operations you would perform using Report Server and Web Portal whereas you will use WMI API for operations you would perform using Reporting Services Configuration Manager. +All of the APIs used by this project are publicly available. There are 2 types of APIs used in this repository: SOAP and WMI. You can find more details about the SOAP API at https://msdn.microsoft.com/en-us/library/ms154052.aspx and the WMI API at https://msdn.microsoft.com/en-us/library/ms152836.aspx. In general, you will use SOAP API for operations you would perform using Report Server and Web Portal whereas you will use WMI API for operations you would perform using Reporting Services Configuration Manager. ## Local testing and development To verify the versions installed ```powershell -Get-Module -ListAvailable | where Name -eq "reportingservicestools" +Get-Module -ListAvailable -Name ReportingServicesTools ``` After you clone the repo you can make local changes and install them in your local machine with @@ -128,21 +148,26 @@ For running tests locally you need a local default instance of SQL Server Repor To install Pester execute ```powershell -Install-Module -Name Pester +Install-Module -Name Pester -RequiredVersion 4.10.1 ``` -To excute the tests run (this will execute the CatalogItems test only which doesn't change the local Reporting Services Installation) +To execute the tests run (this will execute the CatalogItems test only which doesn't change the local Reporting Services Installation) ```powershell .\Test.ps1 ``` +## Import Known Issue +Workaround for newer versions of PowerShell (version 7.0+) +```powershell +Import-Module ReportingServicesTools -UseWindowsPowerShell +``` ## Style Guidelines -If you have any scripts you would like to share, we request you to please format your scripts according to the guidelines created by the team behind the DSC Resource Kit. (https://github.com/PowerShell/DscResources/blob/master/StyleGuidelines.md) and the PowerShell verbs https://msdn.microsoft.com/en-us/library/ms714428(v=vs.85).aspx +If you have any scripts you would like to share, we request you to please format your scripts according to the guidelines created by the team behind the DSC Resource Kit. (https://github.com/PowerShell/DscResources/blob/master/StyleGuidelines.md) and the PowerShell verbs (https://learn.microsoft.com/en-us/powershell/scripting/developer/cmdlet/approved-verbs-for-windows-powershell-commands) ## Contributions -For contributions please provide the minimum possible increment per Pull Request so it can reviewed and merged quickly. +For contributions please provide the minimum possible increment per Pull Request so it can be reviewed and merged quickly. ## Code of Conduct diff --git a/ReportingServicesTools/Functions/Admin/Rest/Get-RsRestPublicServerSetting.ps1 b/ReportingServicesTools/Functions/Admin/Rest/Get-RsRestPublicServerSetting.ps1 new file mode 100644 index 00000000..fa34767a --- /dev/null +++ b/ReportingServicesTools/Functions/Admin/Rest/Get-RsRestPublicServerSetting.ps1 @@ -0,0 +1,95 @@ +function Get-RsRestPublicServerSetting +{ + <# + .SYNOPSIS + This function gets the public settings of the RS server. + + .DESCRIPTION + This function gets the value of the specified property from the public settings of the RS server. + + .PARAMETER Property + Specify the name of the property. + + .PARAMETER ReportPortalUri + Specify the Report Portal URL to your SQL Server Reporting Services Instance. + + .PARAMETER RestApiVersion + Specify the version of REST Endpoint to use. Valid values are: "v2.0". + + .PARAMETER Credential + Specify the credentials to use when connecting to the Report Server. + + .PARAMETER WebSession + Specify the session to be used when making calls to REST Endpoint. + + .EXAMPLE + Get-RsRestPublicServerSetting -Property "MaxFileSizeMb" + Description + ----------- + Gets the value of the property "MaxFileSizeMb" from the Report Server located at http://localhost/reports. + #> + + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $True)] + [string] + $Property, + + [string] + $ReportPortalUri, + + [Alias('ApiVersion')] + [ValidateSet("v2.0")] + [string] + $RestApiVersion = "v2.0", + + [Alias('ReportServerCredentials')] + [System.Management.Automation.PSCredential] + $Credential, + + [Microsoft.PowerShell.Commands.WebRequestSession] + $WebSession + ) + Begin + { + $WebSession = New-RsRestSessionHelper -BoundParameters $PSBoundParameters + if ($null -ne $WebSession.Credentials -and $null -eq $Credential) { + Write-Verbose "Using credentials from WebSession" + $Credential = New-Object System.Management.Automation.PSCredential "$($WebSession.Credentials.UserName)@$($WebSession.Credentials.Domain)", $WebSession.Credentials.SecurePassword + } + $ReportPortalUri = Get-RsPortalUriHelper -WebSession $WebSession + $systemPropertiesUri = $ReportPortalUri + "api/$RestApiVersion/System/Properties?properties={0}" + } + Process + { + try + { + Write-Verbose "Getting server configuration - $Property" + + $uri = [String]::Format($systemPropertiesUri, $Property) + + if ($Credential -ne $null) + { + $response = Invoke-RestMethod -Uri $uri -Method Get -WebSession $WebSession -Credential $Credential -Verbose:$false + } + else + { + $response = Invoke-RestMethod -Uri $uri -Method Get -WebSession $WebSession -UseDefaultCredentials -Verbose:$false + } + + if ($response -ne $null -and $response.value -ne $null -and $response.value[0] -ne $null -and $response.value[0].Name -eq $Property) + { + return $response.value[0].Value + } + else + { + return $null + } + } + catch + { + Write-Error (New-Object System.Exception("Failed to get server setting: $($_.Exception.Message)", $_.Exception)) + } + } +} diff --git a/ReportingServicesTools/Functions/Admin/Restore-RsEncryptionKey.ps1 b/ReportingServicesTools/Functions/Admin/Restore-RsEncryptionKey.ps1 index 2da9d4bb..4bbf8e53 100644 --- a/ReportingServicesTools/Functions/Admin/Restore-RsEncryptionKey.ps1 +++ b/ReportingServicesTools/Functions/Admin/Restore-RsEncryptionKey.ps1 @@ -74,17 +74,21 @@ function Restore-RSEncryptionKey if ($PSCmdlet.ShouldProcess((Get-ShouldProcessTargetWmi -BoundParameters $PSBoundParameters), "Restore encryptionkey from file $KeyPath")) { $rsWmiObject = New-RsConfigurationSettingObjectHelper -BoundParameters $PSBoundParameters - - $KeyPath = Resolve-Path $KeyPath + + $KeyPath = (Resolve-Path $KeyPath).ProviderPath $reportServerService = 'ReportServer' if ($rsWmiObject.InstanceName -ne "MSSQLSERVER") { - if($rsWmiObject.InstanceName -eq "PBIRS") + if ($rsWmiObject.InstanceName -eq "PBIRS") { $reportServerService = 'PowerBIReportServer' } + elseif ($rsWmiObject.InstanceName -eq "SSRS") + { + $reportServerService = 'SQLServerReportingServices' + } else { $reportServerService = $reportServerService + '$' + $rsWmiObject.InstanceName diff --git a/ReportingServicesTools/Functions/Admin/Set-RsDatabase.ps1 b/ReportingServicesTools/Functions/Admin/Set-RsDatabase.ps1 index 707f490b..8c27e1e7 100644 --- a/ReportingServicesTools/Functions/Admin/Set-RsDatabase.ps1 +++ b/ReportingServicesTools/Functions/Admin/Set-RsDatabase.ps1 @@ -38,6 +38,20 @@ function Set-RsDatabase .PARAMETER DatabaseServerName Specify the database server name. (e.g. localhost, MyMachine\Sql2016, etc.) + .PARAMETER Encrypt + Specify the encryption type to use when connecting to SQL Server. + Accepted values: Mandatory, Optional, Strict. + IMPORTANT: If supported by the Invoke-Sqlcmd cmdlet version in use, but not specified, the default value is Mandatory. + Using this parameter requires PowerShell SQLServer module version 22 or higher. + + .PARAMETER TrustServerCertificate + Specify this switch to bypass the server certificate validation. + Using this parameter requires PowerShell SQLServer module version 22 or higher. + + .PARAMETER HostNameInCertificate + Specify the host name to be used in validating the SQL Server TLS/SSL certificate. + Using this parameter requires PowerShell SQLServer module version 22 or higher. + .PARAMETER IsRemoteDatabaseServer Specify this switch if the database server is on a different machine than the machine Reporting Services is running on. @@ -86,6 +100,16 @@ function Set-RsDatabase [string] $DatabaseServerName, + [ValidateSet("Mandatory", "Optional", "Strict")] + [string] + $Encrypt, + + [switch] + $TrustServerCertificate, + + [string] + $HostNameInCertificate, + [switch] $IsRemoteDatabaseServer, @@ -133,6 +157,14 @@ function Set-RsDatabase { $rsWmiObject = New-RsConfigurationSettingObjectHelper -BoundParameters $PSBoundParameters + $supportSQLServerV22Parameters = (Get-InstalledModule -Name "SQLServer" -MinimumVersion 22.0 -ErrorAction SilentlyContinue) -ne $null + $containsSQLServerV22Parameters = $PSBoundParameters.ContainsKey("Encrypt") -or $TrustServerCertificate -or $PSBoundParameters.ContainsKey("HostNameInCertificate") + + if ($containsSQLServerV22Parameters -and -not $supportSQLServerV22Parameters) + { + throw "The current version of Invoke-Sqlcmd cmdlet used in this script doesn't support -Encrypt, -TrustServerCertificate and -HostNameInCertificate parameters. Consider installing SQLServer module version 22 or higher and restarting PowerShell to use the script with these parameters." + } + #region Validating authentication and normalizing credentials $username = '' $password = $null @@ -178,6 +210,37 @@ function Set-RsDatabase } #endregion Validating admin authentication and normalizing credentials + #region Composing general parameters for Invoke-Sqlcmd cmdlet + $generalParameters = @{ + ServerInstance = $DatabaseServerName + QueryTimeout = $QueryTimeout + ErrorAction = "Stop" + } + + if ($isSQLAdminAccount) + { + $generalParameters.add("Username", $adminUsername) + $generalParameters.add("Password", $adminPassword) + } + + if ($containsSQLServerV22Parameters) + { + if ($PSBoundParameters.ContainsKey("Encrypt")) + { + $generalParameters.add("Encrypt", $Encrypt) + } + + if ($TrustServerCertificate) + { + $generalParameters.add("TrustServerCertificate", $true) + } + + if ($PSBoundParameters.ContainsKey("HostNameInCertificate")) + { + $generalParameters.add("HostNameInCertificate", $HostNameInCertificate) + } + } + #endregion Composing general parameters for Invoke-Sqlcmd cmdlet #region Create Database if necessary if (-not $IsExistingDatabase) @@ -202,13 +265,13 @@ function Set-RsDatabase Write-Verbose "Executing database creation script..." try { - if ($isSQLAdminAccount) + if ($supportSQLServerV22Parameters) { - Invoke-Sqlcmd -ServerInstance $DatabaseServerName -Query $SQLScript -QueryTimeout $QueryTimeout -ErrorAction Stop -Username $adminUsername -Password $adminPassword + SQLServer\Invoke-Sqlcmd @generalParameters -Query $SQLScript } else { - Invoke-Sqlcmd -ServerInstance $DatabaseServerName -Query $SQLScript -QueryTimeout $QueryTimeout -ErrorAction Stop + Invoke-Sqlcmd @generalParameters -Query $SQLScript } } catch @@ -240,13 +303,13 @@ function Set-RsDatabase Write-Verbose "Executing database rights script..." try { - if ($isSQLAdminAccount) + if ($supportSQLServerV22Parameters) { - Invoke-Sqlcmd -ServerInstance $DatabaseServerName -Query $SQLScript -QueryTimeout $QueryTimeout -ErrorAction Stop -Username $adminUsername -Password $adminPassword + SQLServer\Invoke-Sqlcmd @generalParameters -Query $SQLScript } else { - Invoke-Sqlcmd -ServerInstance $DatabaseServerName -Query $SQLScript -QueryTimeout $QueryTimeout -ErrorAction Stop + Invoke-Sqlcmd @generalParameters -Query $SQLScript } } catch diff --git a/ReportingServicesTools/Functions/Admin/Set-RsDatabaseCredentials.ps1 b/ReportingServicesTools/Functions/Admin/Set-RsDatabaseCredentials.ps1 index 3b31d09f..1ee579c4 100644 --- a/ReportingServicesTools/Functions/Admin/Set-RsDatabaseCredentials.ps1 +++ b/ReportingServicesTools/Functions/Admin/Set-RsDatabaseCredentials.ps1 @@ -17,6 +17,20 @@ function Set-RsDatabaseCredentials Specify the credentials to use when connecting to the SQL Server. Note: This parameter will be ignored whenever DatabaseCredentialType is set to ServiceAccount! + .PARAMETER Encrypt + Specify the encryption type to use when connecting to SQL Server. + Accepted values: Mandatory, Optional, Strict. + If supported, but not specified, the default value is Mandatory. + Using this parameter requires PowerShell SQLServer module version 22 or higher. + + .PARAMETER TrustServerCertificate + Specify this switch to bypass the server certificate validation. + Using this parameter requires PowerShell SQLServer module version 22 or higher. + + .PARAMETER HostNameInCertificate + Specify the host name to be used in validating the SQL Server TLS/SSL certificate. + Using this parameter requires PowerShell SQLServer module version 22 or higher. + .PARAMETER IsRemoteDatabaseServer Specify this parameter when the database server is on a different machine than the machine Reporting Services is on. @@ -67,6 +81,16 @@ function Set-RsDatabaseCredentials [System.Management.Automation.PSCredential] $DatabaseCredential, + [ValidateSet("Mandatory", "Optional", "Strict")] + [string] + $Encrypt, + + [switch] + $TrustServerCertificate, + + [string] + $HostNameInCertificate, + [switch] $IsRemoteDatabaseServer, @@ -92,6 +116,14 @@ function Set-RsDatabaseCredentials { $rsWmiObject = New-RsConfigurationSettingObjectHelper -BoundParameters $PSBoundParameters + $supportSQLServerV22Parameters = (Get-InstalledModule -Name "SQLServer" -MinimumVersion 22.0 -ErrorAction SilentlyContinue) -ne $null + $containsSQLServerV22Parameters = $PSBoundParameters.ContainsKey("Encrypt") -or $TrustServerCertificate -or $PSBoundParameters.ContainsKey("HostNameInCertificate") + + if ($containsSQLServerV22Parameters -and -not $supportSQLServerV22Parameters) + { + throw "The current version of Invoke-Sqlcmd cmdlet used in this script doesn't support -Encrypt, -TrustServerCertificate and -HostNameInCertificate parameters. Consider installing SQLServer module version 22 or higher and restarting PowerShell to use the script with these parameters." + } + #region Validating authentication and normalizing credentials $username = '' $password = $null @@ -135,7 +167,39 @@ function Set-RsDatabaseCredentials Write-Verbose "Executing database rights script..." try { - Invoke-Sqlcmd -ServerInstance $DatabaseServerName -Query $SQLscript -QueryTimeout $QueryTimeout -ErrorAction Stop + $parameters = @{ + ServerInstance = $DatabaseServerName + Query = $SQLScript + QueryTimeout = $QueryTimeout + ErrorAction = "Stop" + } + + if ($containsSQLServerV22Parameters) + { + if ($PSBoundParameters.ContainsKey("Encrypt")) + { + $parameters.add("Encrypt", $Encrypt) + } + + if ($TrustServerCertificate) + { + $parameters.add("TrustServerCertificate", $true) + } + + if ($PSBoundParameters.ContainsKey("HostNameInCertificate")) + { + $parameters.add("HostNameInCertificate", $HostNameInCertificate) + } + } + + if ($supportSQLServerV22Parameters) + { + SQLServer\Invoke-Sqlcmd @parameters + } + else + { + Invoke-Sqlcmd @parameters + } } catch { diff --git a/ReportingServicesTools/Functions/Admin/Set-RsEmailSettings.ps1 b/ReportingServicesTools/Functions/Admin/Set-RsEmailSettings.ps1 index 4abdf884..4c64c153 100644 --- a/ReportingServicesTools/Functions/Admin/Set-RsEmailSettings.ps1 +++ b/ReportingServicesTools/Functions/Admin/Set-RsEmailSettings.ps1 @@ -20,6 +20,9 @@ function Set-RsEmailSettings .PARAMETER SmtpServer Specify the SMTP Server address. + .PARAMETER SMTPUseSSL + Specify the the use of SSL. + .PARAMETER SenderAddress Specify sender email address for the email. @@ -85,6 +88,10 @@ function Set-RsEmailSettings [string] $SmtpServer, + [Parameter(Mandatory = $False)] + [bool] + $SMTPUseSSL = $true, + [Parameter(Mandatory = $True)] [string] $SenderAddress, @@ -148,7 +155,7 @@ function Set-RsEmailSettings try { - $result = $rsWmiObject.SetAuthenticatedEmailConfiguration($true, $SmtpServer, $SenderAddress, $UserName, $Password, $Authentication.Value__, $true) + $result = $rsWmiObject.SetAuthenticatedEmailConfiguration($true, $SmtpServer, $SenderAddress, $UserName, $Password, $Authentication.Value__, $SMTPUseSSL) } catch { diff --git a/ReportingServicesTools/Functions/CatalogItems/Get-RsDeploymentConfig.ps1 b/ReportingServicesTools/Functions/CatalogItems/Get-RsDeploymentConfig.ps1 new file mode 100644 index 00000000..a19e5bdb --- /dev/null +++ b/ReportingServicesTools/Functions/CatalogItems/Get-RsDeploymentConfig.ps1 @@ -0,0 +1,101 @@ +function Get-RsDeploymentConfig { + <# + .SYNOPSIS + This script retrieves a list of deployment configurations from a Reporting Services project file. + + .DESCRIPTION + This function This script retrieves a list of deployment configurations from a Reporting Services project file for deployment to a Power BI Report Server. + + .PARAMETER RsProjectFile + Specify the location of the SSRS project file whose deployment profiles should be fetched. + + .PARAMETER ConfigurationToUse + Specify which configuration to use from the SSRS project file, if you already know it's name. + + .EXAMPLE + Get-RsDeploymentConfig -RsProjectFile 'C:\source\repos\SQL Server Performance Dashboard\SQL Server Performance Dashboard.rptproj' -ConfigurationToUse Release + + FullPath : Release + OverwriteDatasets : False + OverwriteDataSources : False + TargetReportFolder : /SQL Server Performance Dashboard + TargetDatasetFolder : /Datasets + TargetDatasourceFolder : /Data Sources + TargetReportPartFolder : Report Parts + TargetServerURL : http://localhost/reportserver + RsProjectFolder : C:\source\repos\SQL Server Performance Dashboard + + Description + ----------- + Retrieves all deployment settings for the 'Release' deployment configuration of the SQL Server Performance Dashboard.rptproj file. + Then returns an object with all applicable settings from that deployment configuration. + + .EXAMPLE + Get-RsDeploymentConfig -RsProjectFile 'C:\Users\Aaron\source\repos\Finance\Financial Reports\SSRS_FR\SSRS_FR.rptproj' + + Description + ----------- + Retrieves all deployment profiles from the SSRS_FR.rptproj file and allows the user to choose which deployment configuration to use. + Then returns an object with all applicable settings from that deployment configuration. + + .EXAMPLE + $RSConfig = Get-RsDeploymentConfig -RsProjectFile 'C:\Users\Aaron\source\repos\Financial Reports\SSRS_FR\SSRS_FR.rptproj' + + Description + ----------- + Retrieves all deployment profiles from the SSRS_FR.rptproj file and allows the user to choose which deployment configuration to use. + After the selection is made, the applicable settings are stored in the $RSConfig variable. + + .EXAMPLE + $RSConfig = Get-RsDeploymentConfig -RsProjectFile 'C:\Users\Aaron\source\repos\Financial Reports\SSRS_FR\SSRS_FR.rptproj' -ConfigurationToUse Dev01 | + Add-Member -PassThru -MemberType NoteProperty -Name ReportPortal -Value 'http://localhost/PBIRSportal/' + $RSConfig | Deploy-RsProject + + Retrieves all deployment settings for the 'Dev01' deployment configuration, adds a NoteProperty for the ReportPortal to deploy to, + and then deploys all the project files by calling Deploy-RsProject and passing in all the settings in the $RSConfig variable. + #> + param ( + [Parameter(Mandatory = $true)] + [string]$RsProjectFile, + + [Parameter(Mandatory = $false)] + [string]$ConfigurationToUse + ) + + if($ConfigurationToUse){ + [XML]$rptproj = Get-Content $RsProjectFile + $Deployment = $rptproj.Project.PropertyGroup | where { $_.FullPath -eq $ConfigurationToUse } + + $RSConfig = [pscustomobject]@{ + FullPath = $Deployment.FullPath + OverwriteDatasets = $Deployment.OverwriteDatasets + OverwriteDataSources = $Deployment.OverwriteDataSources + TargetReportFolder = ($Deployment.TargetReportFolder).Trimend("/") + TargetDatasetFolder = if ($null -eq $Deployment.TargetDatasetFolder) { $null } else { ($Deployment.TargetDatasetFolder).Trimend("/") } + TargetDatasourceFolder = if ($null -eq $Deployment.TargetDatasourceFolder) { $null } else { ($Deployment.TargetDatasourceFolder).Trimend("/") } + TargetReportPartFolder = if ($null -eq $Deployment.TargetReportPartFolder) { $null } else { ($Deployment.TargetReportPartFolder).Trimend("/") } + TargetServerURL = $Deployment.TargetServerURL + RsProjectFolder = Split-Path -Path $RsProjectFile + } + + return $RSConfig + } + else{[XML]$rptproj = Get-Content $RsProjectFile + $ConfigurationToUse = $rptproj.Project.PropertyGroup.FullPath | ogv -PassThru + $Deployment = $rptproj.Project.PropertyGroup | where { $_.FullPath -eq $ConfigurationToUse } + + $RSConfig = [pscustomobject]@{ + FullPath = $Deployment.FullPath + OverwriteDatasets = $Deployment.OverwriteDatasets + OverwriteDataSources = $Deployment.OverwriteDataSources + TargetReportFolder = ($Deployment.TargetReportFolder).Trimend("/") + TargetDatasetFolder = if ($null -eq $Deployment.TargetDatasetFolder) { $null } else { ($Deployment.TargetDatasetFolder).Trimend("/") } + TargetDatasourceFolder = if ($null -eq $Deployment.TargetDatasourceFolder) { $null } else { ($Deployment.TargetDatasourceFolder).Trimend("/") } + TargetReportPartFolder = if ($null -eq $Deployment.TargetReportPartFolder) { $null } else { ($Deployment.TargetReportPartFolder).Trimend("/") } + TargetServerURL = $Deployment.TargetServerURL + RsProjectFolder = Split-Path -Path $RsProjectFile + } + + return $RSConfig + } +} \ No newline at end of file diff --git a/ReportingServicesTools/Functions/CatalogItems/New-RsDataSource.ps1 b/ReportingServicesTools/Functions/CatalogItems/New-RsDataSource.ps1 index e76ac359..81e60d41 100644 --- a/ReportingServicesTools/Functions/CatalogItems/New-RsDataSource.ps1 +++ b/ReportingServicesTools/Functions/CatalogItems/New-RsDataSource.ps1 @@ -6,101 +6,104 @@ function New-RsDataSource <# .SYNOPSIS This script creates a new data source on Report Server. - + .DESCRIPTION This script creates a new data source on Report Server. - + .PARAMETER RsFolder Specify the location where the data source should be created at. - + .PARAMETER Name Specify the name of the the new data source .PARAMETER Description Specify the description to be added to the data source. - + .PARAMETER Extension Specify the extension of the new data source (e.g. SQL, SQLAZURE, OLEDB, OLEDB-MD, etc.) For full list, please look at \ node in C:\Program Files\Microsoft SQL Server\MSRS{VersionNumber}.{InstanceName}\Reporting Services\ReportServer\RSReportServer.config. - + .PARAMETER ConnectionString Specify the connection string for the new data source. - + .PARAMETER CredentialRetrieval Specify the type of authentication to use: None, Prompt, Integrated, Store. Please view https://msdn.microsoft.com/en-us/library/reportservice2010.datasourcedefinition.credentialretrieval.aspx for more details on each option. - + .PARAMETER DatasourceCredentials Specify the Credentials to use when connecting to the data source. - + .PARAMETER Prompt Specify the prompt to display to user. - + .PARAMETER ImpersonateUser Specify whether to impersonate using the credentials specify when connecting to the data source. You must specify DatasourceCredentials if you specify this switch. - + .PARAMETER WindowsCredentials Specify whether the credentials specified are Windows credentials or not. You must specify DatasourceCredentials if you specify this switch. - + .PARAMETER Disabled Creates this data source in a disabled state. - + .PARAMETER Overwrite Overwrite the old entry, if an existing data source with same name exists at the specified Path. - + + .PARAMETER Hidden + Mark the item as hidden on the destination server. + .PARAMETER ReportServerUri Specify the Report Server URL to your SQL Server Reporting Services Instance. Use the "Connect-RsReportServer" function to set/update a default value. - + .PARAMETER Credential Specify the credentials to use when connecting to the Report Server. Use the "Connect-RsReportServer" function to set/update a default value. - + .PARAMETER Proxy Report server proxy to use. Use "New-RsWebServiceProxy" to generate a proxy object for reuse. Useful when repeatedly having to connect to multiple different Report Server. - + .EXAMPLE New-RsDataSource -RsFolder '/' -Name 'My Data Source' -Extension 'SQL' -ConnectionString 'Data Source=.;Initial Catalog=MyDb;' -CredentialRetrieval 'None' Description ----------- This command will establish a connection to the Report Server located at http://localhost/reportserver using current user's credentials and create a new SQL Server data source called 'My Data Source' at the root folder. When connecting to this data source, it will use not specify any credentials. - + .EXAMPLE New-RsDataSource -RsFolder '/' -Name 'My Data Source' -Extension 'SQL' -ConnectionString 'Data Source=.;Initial Catalog=MyDb;' -CredentialRetrieval 'Integrated' Description ----------- This command will establish a connection to the Report Server located at http://localhost/reportserver using current user's credentials and create a new SQL Server data source called 'My Data Source' at the root folder. When connecting to this data source, it will assume current user's identity. - + .EXAMPLE New-RsDataSource -RsFolder '/' -Name 'My Data Source' -Extension 'SQL' -ConnectionString 'Data Source=.;Initial Catalog=MyDb;' -CredentialRetrieval 'Prompt' -Prompt 'Please enter your username and password' Description ----------- This command will establish a connection to the Report Server located at http://localhost/reportserver using current user's credentials and create a new SQL Server data source called 'My Data Source' at the root folder. When connecting to this data source, it will prompt user for Database credentials. - + .EXAMPLE New-RsDataSource -RsFolder '/' -Name 'My Data Source' -Extension 'SQL' -ConnectionString 'Data Source=.;Initial Catalog=MyDb;' -CredentialRetrieval 'Prompt' -Prompt 'Please enter your username and password' -WindowsCredentials Description ----------- This command will establish a connection to the Report Server located at http://localhost/reportserver using current user's credentials and create a new SQL Server data source called 'My Data Source' at the root folder. When connecting to this data source, it will prompt user for Windows credentials. - + .EXAMPLE New-RsDataSource -RsFolder '/' -Name 'My Data Source' -Extension 'SQL' -ConnectionString 'Data Source=.;Initial Catalog=MyDb;' -CredentialRetrieval 'Store' -DatasourceCredentials 'sa' -ImpersonateUser Description ----------- This command will establish a connection to the Report Server located at http://localhost/reportserver using current user's credentials and create a new SQL Server data source called 'My Data Source' at the root folder. When connecting to this data source, the specified credentials will be treated as Database credentials. - + .EXAMPLE New-RsDataSource -RsFolder '/' -Name 'My Data Source' -Extension 'SQL' -ConnectionString 'Data Source=.;Initial Catalog=MyDb;' -CredentialRetrieval 'Store' -DatasourceCredentials 'sa' -ImpersonateUser -WindowsCredentials Description ----------- This command will establish a connection to the Report Server located at http://localhost/reportserver using current user's credentials and create a new SQL Server data source called 'My Data Source' at the root folder. When connecting to this data source, the specified credentials will be treated as Windows credentials. - + .EXAMPLE New-RsDataSource -RsFolder '/' -Name 'My Data Source' -Extension 'SQL' -ConnectionString 'Data Source=.;Initial Catalog=MyDb;' -CredentialRetrieval 'None' -Overwrite Description ----------- This command will establish a connection to the Report Server located at http://localhost/reportserver using current user's credentials and create a new SQL Server data source called 'My Data Source' at the root folder. If data source already exists, it will be overwriten. - + #> [cmdletbinding()] @@ -110,21 +113,21 @@ function New-RsDataSource [Parameter(Mandatory = $True)] [string] $RsFolder, - + [Parameter(Mandatory = $True)] [string] $Name, [string] $Description, - + [Parameter(Mandatory = $True)] [string] $Extension, [string] $ConnectionString, - + [Parameter(Mandatory = $True)] [ValidateSet("None", "Prompt", "Integrated", "Store")] [string] @@ -147,17 +150,20 @@ function New-RsDataSource [Switch] $Overwrite, - + + [switch] + $Hidden, + [string] $ReportServerUri, - + [Alias('ReportServerCredentials')] [System.Management.Automation.PSCredential] $Credential, - + $Proxy ) - + $Proxy = New-RsWebServiceProxyHelper -BoundParameters $PSBoundParameters if (($CredentialRetrieval -eq 'STORE') -and ($DatasourceCredentials.UserName -eq $null)) @@ -179,11 +185,11 @@ function New-RsDataSource $datasource = New-Object $datasourceDataType $datasource.ConnectString = $ConnectionString - $datasource.Enabled = $true + $datasource.Enabled = $true $datasource.Extension = $Extension $datasource.WindowsCredentials = $WindowsCredentials $datasource.Prompt = $Prompt - + if ($Disabled) { $datasource.Enabled = $false @@ -214,6 +220,14 @@ function New-RsDataSource $additionalProperties.Add($descriptionProperty) } + if ($Hidden) + { + $hiddenProperty = New-Object $propertyDataType + $hiddenProperty.Name = 'Hidden' + $hiddenProperty.Value = $Hidden + $additionalProperties.Add($hiddenProperty) + } + try { Write-Verbose "Creating data source..." diff --git a/ReportingServicesTools/Functions/CatalogItems/Out-RsCatalogItem.ps1 b/ReportingServicesTools/Functions/CatalogItems/Out-RsCatalogItem.ps1 index 32f93c60..61e59491 100644 --- a/ReportingServicesTools/Functions/CatalogItems/Out-RsCatalogItem.ps1 +++ b/ReportingServicesTools/Functions/CatalogItems/Out-RsCatalogItem.ps1 @@ -7,45 +7,45 @@ function Out-RsCatalogItem <# .SYNOPSIS This downloads catalog items from a report server to disk. - + .DESCRIPTION This downloads catalog items from a report server to disk. Currently supported types to download are reports, datasources, datasets and resources. - + .PARAMETER RsItem Path to catalog item to download. - + .PARAMETER Destination Folder to download catalog item to. - + .PARAMETER ReportServerUri Specify the Report Server URL to your SQL Server Reporting Services Instance. Use the "Connect-RsReportServer" function to set/update a default value. - + .PARAMETER Credential Specify the credentials to use when connecting to the Report Server. Use the "Connect-RsReportServer" function to set/update a default value. - + .PARAMETER Proxy Report server proxy to use. Use "New-RsWebServiceProxy" to generate a proxy object for reuse. Useful when repeatedly having to connect to multiple different Report Server. - + .EXAMPLE Out-RsCatalogItem -ReportServerUri 'http://localhost/reportserver_sql2012' -RsItem /Report -Destination C:\reports - + Description ----------- Download catalog item 'Report' to folder 'C:\reports'. .EXAMPLE - Get-RsFolderContent -ReportServerUri http://localhost/ReportServer -RsItem '/SQL Server Performance Dashboard' | - WHERE Name -Like Wait* | + Get-RsFolderContent -ReportServerUri http://localhost/ReportServer -RsItem '/SQL Server Performance Dashboard' | + WHERE Name -Like Wait* | Out-RsCatalogItem -ReportServerUri http://localhost/ReportServer -Destination c:\SQLReports - + Description ----------- - Downloads all catalog items from folder '/SQL Server Performance Dashboard' with a name that starts with 'Wait' to folder 'C:\SQLReports'. + Downloads all catalog items from folder '/SQL Server Performance Dashboard' with a name that starts with 'Wait' to folder 'C:\SQLReports'. #> [CmdletBinding()] param ( @@ -53,29 +53,29 @@ function Out-RsCatalogItem [Parameter(Mandatory = $True, ValueFromPipeline = $true)] [string[]] $RsItem, - - [ValidateScript({ Test-Path $_ -PathType Container})] + + [ValidateScript({ Test-Path -LiteralPath $_ -PathType Container})] [Parameter(Mandatory = $True)] [string] $Destination, - + [string] $ReportServerUri, - + [Alias('ReportServerCredentials')] [System.Management.Automation.PSCredential] $Credential, - + $Proxy ) - + Begin { $Proxy = New-RsWebServiceProxyHelper -BoundParameters $PSBoundParameters - - $DestinationFullPath = Convert-Path $Destination + + $DestinationFullPath = Convert-Path -LiteralPath $Destination } - + Process { #region Processing each path passed to it @@ -90,7 +90,7 @@ function Out-RsCatalogItem { throw (New-Object System.Exception("Failed to retrieve item type of '$item' from proxy: $($_.Exception.Message)", $_.Exception)) } - + switch ($itemType) { "Unknown" @@ -106,7 +106,7 @@ function Out-RsCatalogItem $fileName = "$(($item.Split("/"))[-1])$(Get-FileExtension -TypeName $itemType)" } } - + Write-Verbose "Downloading $item..." try { @@ -117,7 +117,7 @@ function Out-RsCatalogItem throw (New-Object System.Exception("Failed to retrieve item definition of '$item' from proxy: $($_.Exception.Message)", $_.Exception)) } #endregion Retrieving content from Report Server - + #region Writing results to file Write-Verbose "Writing $itemType content to $DestinationFullPath\$fileName..." try @@ -136,15 +136,15 @@ function Out-RsCatalogItem { throw (New-Object System.IO.IOException("Failed to write content to '$DestinationFullPath\$fileName' : $($_.Exception.Message)", $_.Exception)) } - + Write-Verbose "$item was downloaded to $DestinationFullPath\$fileName successfully!" #endregion Writing results to file } #endregion Processing each path passed to it } - + End { - + } } diff --git a/ReportingServicesTools/Functions/CatalogItems/Out-RsFolderContent.ps1 b/ReportingServicesTools/Functions/CatalogItems/Out-RsFolderContent.ps1 index 1898eaff..432eb87e 100644 --- a/ReportingServicesTools/Functions/CatalogItems/Out-RsFolderContent.ps1 +++ b/ReportingServicesTools/Functions/CatalogItems/Out-RsFolderContent.ps1 @@ -7,36 +7,36 @@ function Out-RsFolderContent <# .SYNOPSIS This downloads catalog items from a folder to disk - + .DESCRIPTION This downloads catalog items from a folder server to disk. Currently the script only downloads reports, datasources, datasets and resources. - + .PARAMETER Recurse Recursively download subfolders. - + .PARAMETER RsFolder Path to folder on report server to download catalog items from. - + .PARAMETER Destination Folder to download catalog items to. - + .PARAMETER ReportServerUri Specify the Report Server URL to your SQL Server Reporting Services Instance. Use the "Connect-RsReportServer" function to set/update a default value. - + .PARAMETER Credential Specify the credentials to use when connecting to the Report Server. Use the "Connect-RsReportServer" function to set/update a default value. - + .PARAMETER Proxy Report server proxy to use. Use "New-RsWebServiceProxy" to generate a proxy object for reuse. Useful when repeatedly having to connect to multiple different Report Server. - + .EXAMPLE Out-RsFolderContent -ReportServerUri 'http://localhost/reportserver_sql2012' -RsFolder /MonthlyReports -Destination C:\reports\MonthlyReports - + Description ----------- Downloads catalogitems from /MonthlyReports into folder C:\reports\MonthlyReports @@ -45,36 +45,36 @@ function Out-RsFolderContent param( [switch] $Recurse, - + [Alias('ItemPath', 'Path')] [Parameter(Mandatory = $True)] [string] $RsFolder, - - [ValidateScript({ Test-Path $_ -PathType Container })] + + [ValidateScript({Test-Path -LiteralPath $_ -PathType Container })] [Parameter(Mandatory = $True)] [string] $Destination, - + [string] $ReportServerUri, - + [Alias('ReportServerCredentials')] [System.Management.Automation.PSCredential] $Credential, - + $Proxy ) - + $Proxy = New-RsWebServiceProxyHelper -BoundParameters $PSBoundParameters - + $GetRsFolderContentParam = @{ Proxy = $Proxy RsFolder = $RsFolder Recurse = $Recurse ErrorAction = 'Stop' } - + try { $items = Get-RsFolderContent @GetRsFolderContentParam @@ -83,12 +83,12 @@ function Out-RsFolderContent { throw (New-Object System.Exception("Failed to retrieve items in '$RsFolder': $($_.Exception.Message)", $_.Exception)) } - - $Destination = Convert-Path $Destination - ## Loop for folders first, because we need to ensure folders are written to disk before we - ## attempt to write any files. Otherwise, file writes will randomly fail to write because - ## its folder has not been created yet. + $Destination = Convert-Path -LiteralPath $Destination + + ## Loop for folders first, because we need to ensure folders are written to disk before we + ## attempt to write any files. Otherwise, file writes will randomly fail to write because + ## its folder has not been created yet. ## The Solution was to loop and create all folders then work on the files. ## Basically, create all folders, then write all the files. foreach ($item in $items) @@ -101,25 +101,25 @@ function Out-RsFolderContent $relativePath = Clear-Substring -string $relativePath -substring $RsFolder -position front } $relativePath = $relativePath.Replace("/", "\") - + $newFolder = $Destination + $relativePath Write-Verbose "Creating folder $newFolder" New-Item $newFolder -ItemType Directory -Force | Out-Null Write-Verbose "Folder: $newFolder was created successfully." } } ## End Folder Loop - + ## Loop for non-folders. foreach ($item in $items) - { - if ($item.TypeName -eq "Resource" -or - $item.TypeName -eq "Report" -or - $item.TypeName -eq "DataSource" -or - $item.TypeName -eq "DataSet" -or + { + if ($item.TypeName -eq "Resource" -or + $item.TypeName -eq "Report" -or + $item.TypeName -eq "DataSource" -or + $item.TypeName -eq "DataSet" -or $item.TypeName -eq "Component") { # TODO: REMOVE this comment below. This is the comment that caused me to look for a solution. - # We're relying on the fact that the implementation of Get-RsFolderContent will show us the folder before their content, + # We're relying on the fact that the implementation of Get-RsFolderContent will show us the folder before their content, # when using the -recurse option, so we can assume that any subfolder will be created before we download the items it contains $relativePath = $item.Path if($RsFolder -ne "/") diff --git a/ReportingServicesTools/Functions/CatalogItems/Publish-RsProject.ps1 b/ReportingServicesTools/Functions/CatalogItems/Publish-RsProject.ps1 new file mode 100644 index 00000000..80d09414 --- /dev/null +++ b/ReportingServicesTools/Functions/CatalogItems/Publish-RsProject.ps1 @@ -0,0 +1,164 @@ +function Publish-RsProject +{ + <# + .SYNOPSIS + This command deploys a Reporting Services project to a Power BI Report Server. + + .DESCRIPTION + This function deploys a full SSRS project to a Power BI Report Server. + + .PARAMETER ProjectFolder + Specify the location of the SSRS project file whose deployment profiles should be fetched. + + .EXAMPLE + Get-RsDeploymentConfig -RsProjectFile 'C:\Users\Aaron\source\repos\Finance\Financial Reports\SSRS_FR\SSRS_FR.rptproj' | + Add-Member -PassThru -MemberType NoteProperty -Name ReportPortal -Value 'http://localhost/PBIRSportal/' | + Publish-RsProject + + Description + ----------- + Get-RsDeploymentConfig prompts the user to select which deployment configuration to use from + the 'C:\Users\Aaron\source\repos\Finance\Financial Reports\SSRS_FR\SSRS_FR.rptproj' project file. These settings + are piped to the Add-Member where the ReportPortal property is added and set to 'http://localhost/PBIRSportal/'. + The settings are then piped to the Publish-RsProject function, which deploys all project files using all applicable + settings from the deployment configuration chosen. + + .EXAMPLE + $RSConfig = Get-RsDeploymentConfig -RsProjectFile 'C:\Users\Aaron\source\repos\Financial Reports\SSRS_FR\SSRS_FR.rptproj' -ConfigurationToUse Dev01 + Add-Member -PassThru -MemberType NoteProperty -Name ReportPortal -Value 'http://localhost/PBIRSportal/' + $RSConfig | Publish-RsProject + + Description + ----------- + Retrieves all deployment settings for the 'Dev01' deployment configuration, adds a NoteProperty for the ReportPortal to + deploy to, and then deploys all the project files by calling Publish-RsProject and passing in all the settings in + the $RSConfig variable. + + .EXAMPLE + Publish-RsProject -TargetServerURL 'http://localhost/PBIRServer/' -ReportPortal 'http://localhost/PBIRSportal/' -TargetReportFolder /Finance -TargetDatasourceFolder '/Finance/Data Sources' -TargetDatasetFolder /Finance/DataSets -RsProjectFolder 'C:\Users\Aaron\source\repos\Financial Reports\SSRS_FR\' + + Description + ----------- + Deploys the project files found in the 'C:\Users\Aaron\source\repos\Financial Reports\SSRS_FR\' to the target locations + specified in the parameters list. Use this option when you want to deploy to a location not already listed in + the .rptproj project file. + #> + [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')] + param ( + [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)] + [string]$RsProjectFolder, + + [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true)] + [string]$TargetServerURL, + + [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true)] + [string]$TargetReportFolder, + + [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true)] + [string]$TargetDatasourceFolder, + + [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true)] + [string]$TargetDatasetFolder, + + [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true)] + [string]$TargetReportPartFolder, + + [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true)] + [string]$OverwriteDatasets, + + [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true)] + [string]$OverwriteDataSources, + + [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true)] + [string]$FullPath, + + [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)] + [string]$ReportPortal + ) + + Write-Host "`$RsProjectFolder = '$RsProjectFolder' being used is + This deployment is going to happen using the following settings... + " + $RSConfig | FL + + <# RsFolder Structure + Make sure all the folders needed already exist with the following code. #> + Write-Host " + Beginning deployment. + Building folder structures... + " + if(-not ($TargetReportFolder).StartsWith("/")){$TargetReportFolder = '/'+$TargetReportFolder} + if(-not ($TargetDatasetFolder).StartsWith("/")){$TargetDatasetFolder = '/'+$TargetDatasetFolder} + if(-not ($TargetDatasourceFolder).StartsWith("/")){$TargetDatasourceFolder = '/'+$TargetDatasourceFolder} + + $TargetReportFolder, $TargetDatasourceFolder, $TargetDatasetFolder | + sort -Unique | + foreach { + MakeDeploymentFolders -RsFolder $_ -ReportPortal $ReportPortal + } + + <# Deploy Data Sources #> + Write-Host " + Deploying Data Sources to $($TargetDatasourceFolder)... + " + foreach($RDS in dir -Path $RsProjectFolder -Filter *.rds) + { + try{ Write-Verbose "Checking for $TargetDatasourceFolder/$($_.BaseName)" + Get-RsRestItem -ReportPortalUri $ReportPortal -RsItem "$TargetDatasourceFolder/$($RDS.BaseName)" | ft -AutoSize + } + catch{ Write-Verbose 'Did not find Data Source' + Write-RsRestCatalogItem -Path "$RsProjectFolder\$($RDS.Name)" -ReportPortalUri $ReportPortal -RsFolder $TargetDatasourceFolder + } + } + + <# Deploy Data Sets & set their Data Source References. #> + Write-Host " + Deploying DataSets to $TargetDatasetFolder... + " + dir -Path $RsProjectFolder -Filter *.rsd | + foreach{ + [XML]$dsetref = Get-Content "$RsProjectFolder\$($_.Name)" + $DataSetQuery = $dsetref.SharedDataSet.DataSet.Query + + $DSetConfig = [pscustomobject]@{ + DataSourceReference = $dsetref.SharedDataSet.DataSet.Query.DataSourceReference + CommandText = $dsetref.SharedDataSet.DataSet.Query.CommandText + CommandType = $dsetref.SharedDataSet.DataSet.Query.CommandType + DataSetParameters = $dsetref.SharedDataSet.DataSet.Query.DataSetParameters + } + + Write-RsRestCatalogItem -Path "$RsProjectFolder\$($_.Name)" -ReportPortalUri $ReportPortal -RsFolder $TargetDatasetFolder -Overwrite + Set-RsDataSourceReference -ReportServerUri $TargetServerURL -Path "$TargetDatasetFolder/$($_.BaseName)" -DataSourceName DataSetDataSource -DataSourcePath "$($TargetDatasourceFolder)/$($DSetConfig.DataSourceReference)" + } + + <# Deploy the Reports #> + Write-Host "Deploying the report files to $TargetReportFolder... + " + dir -Path $RsProjectFolder -Filter *.rdl | + foreach{ + $ReportName=$_.BaseName + Write-RsCatalogItem -Path "$RsProjectFolder\$($_.Name)" -ReportServerUri $TargetServerURL -RsFolder $TargetReportFolder -Overwrite + "$($_.BaseName)"; + Get-RsRestItemDataSource -ReportPortalUri $ReportPortal -RsItem "$TargetReportFolder/$ReportName" | + where {$_.IsReference -eq $true} | + foreach{ + Set-RsDataSourceReference -ReportServerUri $TargetServerURL -Path "$TargetReportFolder/$ReportName" -DataSourceName $_.Name -DataSourcePath "$($TargetDatasourceFolder)/$($_.Name)" + } + } + + <# Now read in the DataSet References directly from the report files and set them on the server #> + if($TargetDatasetFolder -ne $TargetReportFolder -and (Get-RsRestFolderContent -ReportPortalUri $ReportPortal -RsFolder $TargetDatasetFolder).Count -gt 0){ + $Reports = dir -Path $RsProjectFolder -Filter *.rdl + + foreach($Report in $Reports) + { + [XML]$ReportDSetRef = Get-Content $Report.FullName + foreach($SDS in $ReportDSetRef.Report.DataSets.DataSet){ + if($SDS.SharedDataSet.SharedDataSetReference){ + Set-RsDataSetReference -ReportServerUri $TargetServerURL -Path "$TargetReportFolder/$($Report.BaseName)" -DataSetName $SDS.Name -DataSetPath "$TargetDatasetFolder/$($SDS.SharedDataSet.SharedDataSetReference)" + } + } + } + } + +} diff --git a/ReportingServicesTools/Functions/CatalogItems/Rest/Get-RsRestCacheRefreshPlan.ps1 b/ReportingServicesTools/Functions/CatalogItems/Rest/Get-RsRestCacheRefreshPlan.ps1 new file mode 100644 index 00000000..ffa52ebe --- /dev/null +++ b/ReportingServicesTools/Functions/CatalogItems/Rest/Get-RsRestCacheRefreshPlan.ps1 @@ -0,0 +1,126 @@ +# Copyright (c) 2020 Microsoft Corporation. All Rights Reserved. +# Licensed under the MIT License (MIT) + +function Get-RsRestCacheRefreshPlan +{ + <# + .SYNOPSIS + This function fetches a CacheRefreshPlan from a Power BI report. + .DESCRIPTION + This function fetches a CacheRefreshPlan from a Power BI report. + .PARAMETER RsReport + Specify the location of the Power BI report for which the CacheRefreshPlan should be fetched. + .PARAMETER ReportPortalUri + Specify the Report Portal URL to your SQL Server Reporting Services Instance. + .PARAMETER RestApiVersion + Specify the version of REST Endpoint to use. Valid values are: "v2.0". + .PARAMETER Credential + Specify the credentials to use when connecting to the Report Server. + .PARAMETER WebSession + Specify the session to be used when making calls to REST Endpoint. + .EXAMPLE + Get-RsRestCacheRefreshPlan -RsReport "/MyReport" + Description + ----------- + Fetches CacheRefreshPlan object for the "MyReport" catalog item found in "/" folder from the Report Server located at http://localhost/reports. + .EXAMPLE + Get-RsRestCacheRefreshPlan -RsReport "/MyReport" -WebSession $session + Description + ----------- + Fetches CacheRefreshPlan object the "MyReport" catalog item found in "/" folder from the Report Server located at specificed WebSession object. + .EXAMPLE + Get-RsRestCacheRefreshPlan -RsReport "/MyReport" -ReportPortalUri http://myserver/reports + Description + ----------- + Fetches CacheRefreshPlan object for the "MyReport" catalog item found in "/" folder from the Report Server located at http://myserver/reports. + .EXAMPLE + Get-RsRestCacheRefreshPlan -RsReport "/Finance" -ReportPortalUri http://myserver/reports + Description + ----------- + Fetches CacheRefreshPlan object for the "Finance" catalog item, which is a Folder object found in "/" folder from the Report Server located at http://myserver/reports. + #> + + [CmdletBinding()] + param( + [Parameter(Mandatory = $True, ValueFromPipelineByPropertyName = $true)] + [Alias('ItemPath','Path', 'RsItem')] + [string] + $RsReport, + + [string] + $ReportPortalUri, + + [Alias('ApiVersion')] + [ValidateSet("v2.0")] + [string] + $RestApiVersion = "v2.0", + + [Alias('ReportServerCredentials')] + [System.Management.Automation.PSCredential] + $Credential, + + [Microsoft.PowerShell.Commands.WebRequestSession] + $WebSession + ) + Begin + { + $WebSession = New-RsRestSessionHelper -BoundParameters $PSBoundParameters + if ($null -ne $WebSession.Credentials -and $null -eq $Credential) { + Write-Verbose "Using credentials from WebSession" + $Credential = New-Object System.Management.Automation.PSCredential "$($WebSession.Credentials.UserName)@$($WebSession.Credentials.Domain)", $WebSession.Credentials.SecurePassword + } + $ReportPortalUri = Get-RsPortalUriHelper -WebSession $WebSession + $CacheRefreshPlanUri = $ReportPortalUri + "api/$RestApiVersion/PowerBIReports({0})/CacheRefreshPlans" + } + Process + { + try + { + Write-Verbose "Fetching metadata for $RsReport..." + if ($Credential -ne $null) + { + $Report = Get-RsItem -ReportPortalUri $ReportPortalUri -RsItem $RsReport -WebSession $WebSession -Credential $Credential -Verbose:$false + } + else + { + $Report = Get-RsItem -ReportPortalUri $ReportPortalUri -RsItem $RsReport -WebSession $WebSession -Verbose:$false + } + + Write-Verbose "Fetching CacheRefreshPlans for $RsReport..." + $CacheRefreshPlanUri = [String]::Format($CacheRefreshPlanUri, $Report.Id) + if ($Credential -ne $null) + { + $response = Invoke-RestMethod -Uri $CacheRefreshPlanUri -Method Get -WebSession $WebSession -Credential $Credential -Verbose:$false + } + else + { + $response = Invoke-RestMethod -Uri $CacheRefreshPlanUri -Method Get -WebSession $WebSession -UseDefaultCredentials -Verbose:$false + } + + $items = $response.value + foreach($item in $items){ + $CacheRefreshPlan = @([pscustomobject]@{ + RsReport = $item.CatalogItemPath + EventType = $item.EventType + LastRunTime = $item.LastRunTime + LastStatus = $item.LastStatus + Description = $item.Description + Id = $item.Id + ScheduleDescription = $item.ScheduleDescription + Owner = $item.Owner + ModifiedBy = $item.ModifiedBy + ModifiedDate = $item.ModifiedDate + Schedule = $item.Schedule + ParameterValues = $item.ParameterValues + }) + $CacheRefreshPlans += $CacheRefreshPlan + } + return $CacheRefreshPlans + } + catch + { + throw (New-Object System.Exception("Failed to get cache refresh plan for '$RsReport': $($_.Exception.Message)", $_.Exception)) + } + } +} +New-Alias -Name "Get-RsCacheRefreshPlan" -Value Get-RsRestCacheRefreshPlan -Scope Global \ No newline at end of file diff --git a/ReportingServicesTools/Functions/CatalogItems/Rest/Get-RsRestCacheRefreshPlanHistory.ps1 b/ReportingServicesTools/Functions/CatalogItems/Rest/Get-RsRestCacheRefreshPlanHistory.ps1 new file mode 100644 index 00000000..8327fa7d --- /dev/null +++ b/ReportingServicesTools/Functions/CatalogItems/Rest/Get-RsRestCacheRefreshPlanHistory.ps1 @@ -0,0 +1,137 @@ +function Get-RsRestCacheRefreshPlanHistory +{ + <# + .SYNOPSIS + This function fetches the history of CacheRefreshPlan(s) from a Power BI report. + + .DESCRIPTION + This function fetches the history of CacheRefreshPlan(s) from a Power BI report. + + .PARAMETER RsReport + Specify the location of the Power BI report for which the CacheRefreshPlans should be fetched. + + .PARAMETER Id + Specify the Id of the CacheRefreshPlan for a Power BI report which should be fetched. + + .PARAMETER ReportPortalUri + Specify the Report Portal URL to your Power BI Report Server Instance. + + .PARAMETER RestApiVersion + Specify the version of REST Endpoint to use. Valid values are: "v2.0". + + .PARAMETER Credential + Specify the credentials to use when connecting to the Report Server. + + .PARAMETER WebSession + Specify the session to be used when making calls to REST Endpoint. + + .EXAMPLE + Get-RsRestCacheRefreshPlanHistory -RsReport "/MyReport" + Description + ----------- + Fetches the history of all CacheRefreshPlans for the "MyReport" catalog item found in "/" folder from the Report Server located at http://localhost/reports. + + .EXAMPLE + Get-RsRestCacheRefreshPlanHistory -RsReport "/MyReport" -WebSession $session + Description + ----------- + Fetches the history of all CacheRefreshPlans for the "MyReport" catalog item found in "/" folder from the Report Server located at specificed WebSession object. + + .EXAMPLE + Get-RsRestCacheRefreshPlanHistory -RsReport "/MyReport" -ReportPortalUri http://myserver/reports + Description + ----------- + Fetches the history of all CacheRefreshPlans for the "MyReport" catalog item found in "/" folder from the Report Server located at http://myserver/reports. + + .EXAMPLE + Get-RsRestCacheRefreshPlanHistory -Id 'f8796f95-31c8-46fe-b184-4677cbbf5abf' -ReportPortalUri http://myserver/reports + Description + ----------- + Fetches the history of all CacheRefreshPlans for the "Finance" report from the Report Server located at http://myserver/reports. + #> + + [CmdletBinding()] + param( + [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true)] + [Alias('ItemPath','Path', 'RsItem')] + [string] + $RsReport, + + [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true)] + [string] + $ReportPortalUri, + + [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true)] + [Alias('CacheRefreshPlan')] + [string] + $Id = $null, + + [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true)] + [Alias('ApiVersion')] + [ValidateSet("v2.0")] + [string] + $RestApiVersion = "v2.0", + + [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true)] + [Alias('ReportServerCredentials')] + [System.Management.Automation.PSCredential] + $Credential, + + [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true)] + [Microsoft.PowerShell.Commands.WebRequestSession] + $WebSession + ) + Begin + { + $WebSession = New-RsRestSessionHelper -BoundParameters $PSBoundParameters + if ($null -ne $WebSession.Credentials -and $null -eq $Credential) { + Write-Verbose "Using credentials from WebSession" + $Credential = New-Object System.Management.Automation.PSCredential "$($WebSession.Credentials.UserName)@$($WebSession.Credentials.Domain)", $WebSession.Credentials.SecurePassword + } + $ReportPortalUri = Get-RsPortalUriHelper -WebSession $WebSession + } + Process + { + + if($RsReport){ + if ($Credential -ne $null) + { + $CachePlans = Get-RsRestCacheRefreshPlan -ReportPortalUri $ReportPortalUri -RsItem $RsReport -Credential $Credential -Verbose:$false + } + else + { + $CachePlans = Get-RsRestCacheRefreshPlan -ReportPortalUri $ReportPortalUri -RsItem $RsReport -WebSession $WebSession -Verbose:$false + } + + foreach($CachePlan in $CachePlans){ + $CacheRefreshPlanUri = $ReportPortalUri + "api/$RestApiVersion/CacheRefreshPlans({0})/History" + $CacheRefreshPlanUri = [String]::Format($CacheRefreshPlanUri, $CachePlan.Id) + Write-Verbose "$CacheRefreshPlanUri" + if ($Credential -ne $null) + { + $response = Invoke-RestMethod -Uri $CacheRefreshPlanUri -Method Get -WebSession $WebSession -Credential $Credential -Verbose:$false + } + else + { + $response = Invoke-RestMethod -Uri $CacheRefreshPlanUri -Method Get -WebSession $WebSession -UseDefaultCredentials + } + return $response.value + } + } + + if($Id){ + $CacheRefreshPlanUri = $ReportPortalUri + "api/$RestApiVersion/CacheRefreshPlans({0})/History" + $CacheRefreshPlanUri = [String]::Format($CacheRefreshPlanUri, $Id) + Write-Verbose "$CacheRefreshPlanUri" + if ($Credential -ne $null) + { + $response = Invoke-RestMethod -Uri $CacheRefreshPlanUri -Method Get -WebSession $WebSession -Credential $Credential -Verbose:$false + } + else + { + $response = Invoke-RestMethod -Uri $CacheRefreshPlanUri -Method Get -WebSession $WebSession -UseDefaultCredentials + } + return $response.value + } + } +} \ No newline at end of file diff --git a/ReportingServicesTools/Functions/CatalogItems/Rest/Get-RsRestDataSource.ps1 b/ReportingServicesTools/Functions/CatalogItems/Rest/Get-RsRestDataSource.ps1 new file mode 100644 index 00000000..dc3e7830 --- /dev/null +++ b/ReportingServicesTools/Functions/CatalogItems/Rest/Get-RsRestDataSource.ps1 @@ -0,0 +1,80 @@ +# Licensed under the MIT License (MIT) + +function Get-RsRestDataSource { + <# + .SYNOPSIS + Fetches a data source from the report server + + .DESCRIPTION + Fetches a data source from the report server + + .PARAMETER RsItem + Specify the path of the data source to fetch + + .PARAMETER ReportPortalUri + Specify the Report Portal URL to your SQL Server Reporting Services Instance. + + .PARAMETER RestApiVersion + Specify the version of REST Endpoint to use. Valid values are: "v2.0". + + .PARAMETER Credential + Specify the credentials to use when connecting to the Report Server. + + .PARAMETER WebSession + Specify the session to be used when making calls to REST Endpoint. + + .EXAMPLE + Get-RsRestDataSource -ReportPortalUri http://localhost/reports -RsItem "/MyDataSource" + + Fetches data source information associated to "MyDataSource" data source found in "/" folder. + The returned objects properties can be found on https://app.swaggerhub.com/apis/microsoft-rs/SSRS/2.0#/DataSource + + .NOTES + General notes + #> + [CmdletBinding()] + param( + [Parameter(Mandatory = $True)] + [Alias('ItemPath','Path')] + [string] + $RsItem, + + [string] + $ReportPortalUri, + + [Alias('ApiVersion')] + [ValidateSet("v2.0")] + [string] + $RestApiVersion = "v2.0", + + [Alias('ReportServerCredentials')] + [System.Management.Automation.PSCredential] + $Credential, + + [Microsoft.PowerShell.Commands.WebRequestSession] + $WebSession + ) + Begin + { + $splatInvokeWebRequest = New-RestSessionHelperSplat -BoundParameters $PSBoundParameters + $ReportPortalUri = Get-RsPortalUriHelper -WebSession $splatInvokeWebRequest.WebSession + $dsItemsUriFormat = $ReportPortalUri + "api/$RestApiVersion/DataSources(Path='{0}')" + } + Process { + try { + $dsItemUri = [String]::Format($dsItemsUriFormat, $RsItem) + + Write-Verbose "Retrieving data source contents $dsItemUri ..." + $response = Invoke-WebRequest @splatInvokeWebRequest -Method 'GET' -Uri $dsItemUri + $item = ConvertFrom-Json $response.Content + + [PSCustomObject] $item | Select-Object -ExcludeProperty '@odata.context','@odata.type' + } catch { + $e = $_ + if ($e.ErrorDetails.Message) { + $ErrorDetail = ($e.ErrorDetails.Message | ConvertFrom-Json).error + } + throw (New-Object System.Exception("Exception while retrieving datasource! $($ErrorDetail.message)", $e.Exception)) + } + } +} \ No newline at end of file diff --git a/ReportingServicesTools/Functions/CatalogItems/Rest/Get-RsRestFolderContent.ps1 b/ReportingServicesTools/Functions/CatalogItems/Rest/Get-RsRestFolderContent.ps1 new file mode 100644 index 00000000..14a1b818 --- /dev/null +++ b/ReportingServicesTools/Functions/CatalogItems/Rest/Get-RsRestFolderContent.ps1 @@ -0,0 +1,157 @@ +# Copyright (c) 2020 Microsoft Corporation. All Rights Reserved. +# Licensed under the MIT License (MIT) +function Get-RsRestFolderContent +{ + <# + .SYNOPSIS + This function fetches data sources related to a catalog item from the Report Server. + + .DESCRIPTION + This function fetches data sources related to a catalog item from the Report Server, using the REST API. + + .PARAMETER RsFolder + Specify the location of the catalog item whose data sources should be fetched. + + .PARAMETER ReportPortalUri + Specify the Report Portal URL to your SQL Server Reporting Services Instance. + + .PARAMETER RestApiVersion + Specify the version of REST Endpoint to use. Valid values are: "v2.0". + + .PARAMETER Credential + Specify the credentials to use when connecting to the Report Server. + + .PARAMETER WebSession + Specify the session to be used when making calls to REST Endpoint. + + .EXAMPLE + Get-RsRestFolderContent -ReportPortalUri 'http://localhost/reportserver_sql2012' -RsFolder / + + Description + ----------- + List all items directly under the root folder + + .EXAMPLE + Get-RsRestFolderContent -ReportServerUri http://localhost/ReportServer -RsFolder / -Recurse + + Description + ----------- + Lists all items directly under the root of the SSRS instance and recursively under all sub-folders. + #> + + [CmdletBinding()] + param( + [Parameter(Mandatory = $True)] + [Alias('ItemPath','Path')] + [string] + $RsFolder, + + [switch] + $Recurse, + + [string] + $ReportPortalUri, + + [Alias('ApiVersion')] + [ValidateSet("v2.0")] + [string] + $RestApiVersion = "v2.0", + + [Alias('ReportServerCredentials')] + [System.Management.Automation.PSCredential] + $Credential, + + [Microsoft.PowerShell.Commands.WebRequestSession] + $WebSession + ) + Begin + { + $WebSession = New-RsRestSessionHelper -BoundParameters $PSBoundParameters + if ($null -ne $WebSession.Credentials -and $null -eq $Credential) { + Write-Verbose "Using credentials from WebSession" + $Credential = New-Object System.Management.Automation.PSCredential "$($WebSession.Credentials.UserName)@$($WebSession.Credentials.Domain)", $WebSession.Credentials.SecurePassword + } + $ReportPortalUri = Get-RsPortalUriHelper -WebSession $WebSession + $catalogItemsUri = $ReportPortalUri + "api/$RestApiVersion/Folders(Path='{0}')/CatalogItems?`$expand=Properties" + } + Process + { + try + { + Write-Verbose "Fetching metadata for $RsFolder..." + $url = [String]::Format($catalogItemsUri, $RsFolder) + if ($Credential -ne $null) + { + $response = Invoke-WebRequest -Uri $url -Method Get -WebSession $WebSession -Credential $Credential -UseBasicParsing -Verbose:$false + } + else + { + $response = Invoke-WebRequest -Uri $url -Method Get -WebSession $WebSession -UseDefaultCredentials -UseBasicParsing -Verbose:$false + } + + $catalogItems = (ConvertFrom-Json $response.Content).value + foreach ($catalogItem in $catalogItems) + { + if ($catalogItem.Type -eq "Folder") + { + if ($Recurse) + { + # check for subfolders + $subFolderPath = "$RsFolder/$($catalogItem.Name)" + Write-Verbose "Searching folder $($subFolderPath)" + + # get contents of the subfolders + Get-RsRestFolderContent -RsFolder $catalogItem.Path -ReportPortalUri $ReportPortalUri -RestApiVersion $RestApiVersion -Credential $Credential -WebSession $WebSession -Recurse + + [pscustomobject]@{ + Type = $catalogItem.Type + Name = $catalogItem.Name + Size = $catalogItem.Size + ModifiedBy = $catalogItem.ModifiedBy + ModifiedDate = $catalogItem.ModifiedDate + Hidden = $catalogItem.Hidden + Path = $catalogItem.Path + Id = $catalogItem.Id + Description = $catalogItem.Description + } + } + else + { + [pscustomobject]@{ + Type = $catalogItem.Type + Name = $catalogItem.Name + Size = $catalogItem.Size + ModifiedBy = $catalogItem.ModifiedBy + ModifiedDate = $catalogItem.ModifiedDate + Hidden = $catalogItem.Hidden + Path = $catalogItem.Path + Id = $catalogItem.Id + Description = $catalogItem.Description + } + } + } + else + { + #display contents of $catalogItem + Write-Verbose "Parsing metadata for child item $($catalogItem.Name)..." + + [pscustomobject]@{ + Type = $catalogItem.Type + Name = $catalogItem.Name + Size = $catalogItem.Size + ModifiedBy = $catalogItem.ModifiedBy + ModifiedDate = $catalogItem.ModifiedDate + Hidden = $catalogItem.Hidden + Path = $catalogItem.Path + Id = $catalogItem.Id + Description = $catalogItem.Description + } + } + } + } + catch + { + Write-Warning "Failed to get content for '$RsFolder': $($_.Exception.Message)" + } + } +} diff --git a/ReportingServicesTools/Functions/CatalogItems/Rest/Get-RsRestItem.ps1 b/ReportingServicesTools/Functions/CatalogItems/Rest/Get-RsRestItem.ps1 new file mode 100644 index 00000000..2841bd8b --- /dev/null +++ b/ReportingServicesTools/Functions/CatalogItems/Rest/Get-RsRestItem.ps1 @@ -0,0 +1,118 @@ +# Copyright (c) 2020 Microsoft Corporation. All Rights Reserved. +# Licensed under the MIT License (MIT) + +function Get-RsRestItem +{ + <# + .SYNOPSIS + This function fetches a catalog item from the Report Server + .DESCRIPTION + This function fetches a catalog item from the Report Server using the REST API. + .PARAMETER RsItem + Specify the location of the catalog item which should be fetched. + .PARAMETER ReportPortalUri + Specify the Report Portal URL to your SQL Server Reporting Services Instance. + .PARAMETER RestApiVersion + Specify the version of REST Endpoint to use. Valid values are: "v2.0". + .PARAMETER Credential + Specify the credentials to use when connecting to the Report Server. + .PARAMETER WebSession + Specify the session to be used when making calls to REST Endpoint. + .EXAMPLE + Get-RsRestItem -RsItem "/MyReport" + Description + ----------- + Fetches item object "MyReport" catalog item found in "/" folder from the Report Server located at http://localhost/reports. + .EXAMPLE + Get-RsRestItem -RsItem "/MyReport" -WebSession $session + Description + ----------- + Fetches item object "MyReport" catalog item found in "/" folder from the Report Server located at specificed WebSession object. + .EXAMPLE + Get-RsRestItem -RsItem "/MyReport" -ReportPortalUri http://myserver/reports + Description + ----------- + Fetches catalog item "MyReport" catalog item found in "/" folder from the Report Server located at http://myserver/reports. + .EXAMPLE + Get-RsRestItem -RsItem "/Finance" -ReportPortalUri http://myserver/reports + Description + ----------- + Fetches item "Finance" catalog item, which is a Folder object found in "/" folder from the Report Server located at http://myserver/reports. + #> + + [CmdletBinding()] + param( + [Parameter(Mandatory = $True)] + [Alias('ItemPath','Path')] + [string] + $RsItem, + + [string] + $ReportPortalUri, + + [Alias('ApiVersion')] + [ValidateSet("v2.0")] + [string] + $RestApiVersion = "v2.0", + + [Alias('ReportServerCredentials')] + [System.Management.Automation.PSCredential] + $Credential, + + [Microsoft.PowerShell.Commands.WebRequestSession] + $WebSession + ) + Begin + { + $WebSession = New-RsRestSessionHelper -BoundParameters $PSBoundParameters + if ($null -ne $WebSession.Credentials -and $null -eq $Credential) { + Write-Verbose "Using credentials from WebSession" + $Credential = New-Object System.Management.Automation.PSCredential "$($WebSession.Credentials.UserName)@$($WebSession.Credentials.Domain)", $WebSession.Credentials.SecurePassword + } + $ReportPortalUri = Get-RsPortalUriHelper -WebSession $WebSession + $catalogItemsUri = $ReportPortalUri + "api/$RestApiVersion/CatalogItems(Path='{0}')" + } + Process + { + try + { + Write-Verbose "Fetching metadata for $RsItem..." + $catalogItemsUri = [String]::Format($catalogItemsUri, $RsItem) + if ($Credential -ne $null) + { + $response = Invoke-WebRequest -Uri $catalogItemsUri -Method Get -WebSession $WebSession -Credential $Credential -UseBasicParsing -Verbose:$false + } + else + { + $response = Invoke-WebRequest -Uri $catalogItemsUri -Method Get -WebSession $WebSession -UseDefaultCredentials -UseBasicParsing -Verbose:$false + } + + $item = ConvertFrom-Json $response.Content + $itemType = $item.Type + return [pscustomobject]@{ + Type = $item.Type + Name = $item.Name + Size = $item.Size + CreatedBy = $item.CreatedBy + CreatedDate = $item.CreatedDate + ModifiedBy = $item.ModifiedBy + ModifiedDate = $item.ModifiedDate + Hidden = $item.Hidden + Path = $item.Path + Id = $item.Id + ParentFolderId = $item.ParentFolderId + Description = $item.Description + ContentType = $item.ContentType + Content = $item.Content + IsFavorite = $item.IsFavorite + Roles = $item.Roles + } + } + catch + { + throw (New-Object System.Exception("Failed to get for '$RsItem': $($_.Exception.Message)", $_.Exception)) + } + } +} +New-Alias -Name "Get-RsCatalogItem" -Value Get-RsRestItem -Scope Global +New-Alias -Name "Get-RsItem" -Value Get-RsRestItem -Scope Global diff --git a/ReportingServicesTools/Functions/CatalogItems/Rest/Get-RsRestItemDataModelParameters.ps1 b/ReportingServicesTools/Functions/CatalogItems/Rest/Get-RsRestItemDataModelParameters.ps1 new file mode 100644 index 00000000..45d2589d --- /dev/null +++ b/ReportingServicesTools/Functions/CatalogItems/Rest/Get-RsRestItemDataModelParameters.ps1 @@ -0,0 +1,124 @@ +# Copyright (c) 2017 Microsoft Corporation. All Rights Reserved. +# Licensed under the MIT License (MIT) + +function Get-RsRestItemDataModelParameter +{ + <# + .SYNOPSIS + This function fetches the Data Model Parameters related to a Catalog Item report from the Report Server. This is currently only applicable to Power BI Reports and only from ReportServer October/2020 or higher. + + .DESCRIPTION + This function fetches the Data Model Parameters related to a Catalog Item report from the Report Server. This is currently only applicable to Power BI Reports and only from ReportServer October/2020 or higher. + + .PARAMETER RsItem + Specify the location of the catalog item whose data model parameters should be fetched. + + .PARAMETER ReportPortalUri + Specify the Report Portal URL to your Power Bi Report Server Instance. + + .PARAMETER RestApiVersion + Specify the version of REST Endpoint to use. Valid values are: "v2.0". + + .PARAMETER Credential + Specify the credentials to use when connecting to the Report Server. + + .PARAMETER WebSession + Specify the session to be used when making calls to REST Endpoint. + + .EXAMPLE + Get-RsRestItemDataModelParameter -RsItem "/MyPbixReport1" + + Description + ----------- + Fetches data model parameter information associated to "MyReport" catalog item found in "/" folder from the Report Server located at http://localhost/reports. + + .EXAMPLE + Get-RsRestItemDataModelParameter -RsItem "/MyReport" -WebSession $session + + Description + ----------- + Fetches data model parameter information associated to "MyReport" catalog item found in "/" folder from the Report Server located at specificed WebSession object. + + .EXAMPLE + Get-RsRestItemDataModelParameter -RsItem "/MyReport" -ReportPortalUri http://myserver/reports + + Description + ----------- + Fetches data model parameter information associated to "MyReport" catalog item found in "/" folder from the Report Server located at http://myserver/reports. + + .LINK + https://docs.microsoft.com/en-us/power-bi/report-server/connect-data-source-apis + #> + + [CmdletBinding()] + param( + [Parameter(Mandatory = $True)] + [string] + $RsItem, + + [string] + $ReportPortalUri, + + [ValidateSet("v2.0")] + [string] + $RestApiVersion = "v2.0", + + [System.Management.Automation.PSCredential] + $Credential, + + [Microsoft.PowerShell.Commands.WebRequestSession] + $WebSession + ) + Begin + { + $WebSession = New-RsRestSessionHelper -BoundParameters $PSBoundParameters + if ($null -ne $WebSession.Credentials -and $null -eq $Credential) { + Write-Verbose "Using credentials from WebSession" + $Credential = New-Object System.Management.Automation.PSCredential "$($WebSession.Credentials.UserName)@$($WebSession.Credentials.Domain)", $WebSession.Credentials.SecurePassword + } + $ReportPortalUri = Get-RsPortalUriHelper -WebSession $WebSession + $catalogItemsUri = $ReportPortalUri + "api/$RestApiVersion/CatalogItems(Path='{0}')" + $parametersUri = $ReportPortalUri + "api/$RestApiVersion/{0}(Path='{1}')?`$expand=DataModelParameters" + } + Process + { + try + { + Write-Verbose "Fetching metadata for $RsItem..." + $catalogItemsUri = [String]::Format($catalogItemsUri, $RsItem) + if ($Credential -ne $null) + { + $response = Invoke-WebRequest -Uri $catalogItemsUri -Method Get -WebSession $WebSession -Credential $Credential -UseBasicParsing -Verbose:$false + } + else + { + $response = Invoke-WebRequest -Uri $catalogItemsUri -Method Get -WebSession $WebSession -UseDefaultCredentials -UseBasicParsing -Verbose:$false + } + + $item = ConvertFrom-Json $response.Content + $itemType = $item.Type + + Write-Verbose "Fetching parameters for $RsItem..." + $parametersUri = [String]::Format($parametersUri, $itemType + "s", $RsItem) + + if ($Credential -ne $null) + { + $paramResponse = Invoke-WebRequest -Uri $parametersUri -Method Get -WebSession $WebSession -Credential $Credential -UseBasicParsing -Verbose:$false + } + else + { + $paramResponse = Invoke-WebRequest -Uri $parametersUri -Method Get -WebSession $WebSession -UseDefaultCredentials -UseBasicParsing -Verbose:$false + } + + Write-Verbose $paramResponse + $itemWithDataSources = ConvertFrom-Json $paramResponse.Content + return $itemWithDataSources.DataModelParameters + } + catch + { + Write-Error "Error fetching parameters for '$RsItem': $($_.Exception.Message)" + throw (New-Object System.Exception("Failed to get data model parameters for '$RsItem': $($_.Exception.Message)", $_.Exception)) + } + } +} +New-Alias -Name "Get-RsRestItemDataModelParameters" -Value Get-RsRestItemDataModelParameter -Scope Global diff --git a/ReportingServicesTools/Functions/CatalogItems/Rest/Get-RsRestItemDataSource.ps1 b/ReportingServicesTools/Functions/CatalogItems/Rest/Get-RsRestItemDataSource.ps1 index c962edb6..0db76fe9 100644 --- a/ReportingServicesTools/Functions/CatalogItems/Rest/Get-RsRestItemDataSource.ps1 +++ b/ReportingServicesTools/Functions/CatalogItems/Rest/Get-RsRestItemDataSource.ps1 @@ -45,6 +45,9 @@ function Get-RsRestItemDataSource Description ----------- Fetches item information (including data sources) associated to "MyReport" catalog item found in "/" folder from the Report Server located at http://myserver/reports. + + .LINK + https://docs.microsoft.com/en-us/power-bi/report-server/connect-data-source-apis-pre-oct-2020 #> [CmdletBinding()] @@ -72,6 +75,10 @@ function Get-RsRestItemDataSource Begin { $WebSession = New-RsRestSessionHelper -BoundParameters $PSBoundParameters + if ($null -ne $WebSession.Credentials -and $null -eq $Credential) { + Write-Verbose "Using credentials from WebSession" + $Credential = New-Object System.Management.Automation.PSCredential "$($WebSession.Credentials.UserName)@$($WebSession.Credentials.Domain)", $WebSession.Credentials.SecurePassword + } $ReportPortalUri = Get-RsPortalUriHelper -WebSession $WebSession $catalogItemsUri = $ReportPortalUri + "api/$RestApiVersion/CatalogItems(Path='{0}')" $dataSourcesUri = $ReportPortalUri + "api/$RestApiVersion/{0}(Path='{1}')?`$expand=DataSources" @@ -84,11 +91,11 @@ function Get-RsRestItemDataSource $catalogItemsUri = [String]::Format($catalogItemsUri, $RsItem) if ($Credential -ne $null) { - $response = Invoke-WebRequest -Uri $catalogItemsUri -Method Get -WebSession $WebSession -Credential $Credential -Verbose:$false + $response = Invoke-WebRequest -Uri $catalogItemsUri -Method Get -WebSession $WebSession -Credential $Credential -UseBasicParsing -Verbose:$false } else { - $response = Invoke-WebRequest -Uri $catalogItemsUri -Method Get -WebSession $WebSession -UseDefaultCredentials -Verbose:$false + $response = Invoke-WebRequest -Uri $catalogItemsUri -Method Get -WebSession $WebSession -UseDefaultCredentials -UseBasicParsing -Verbose:$false } $item = ConvertFrom-Json $response.Content @@ -99,11 +106,11 @@ function Get-RsRestItemDataSource if ($Credential -ne $null) { - $response = Invoke-WebRequest -Uri $dataSourcesUri -Method Get -WebSession $WebSession -Credential $Credential -Verbose:$false + $response = Invoke-WebRequest -Uri $dataSourcesUri -Method Get -WebSession $WebSession -Credential $Credential -UseBasicParsing -Verbose:$false } else { - $response = Invoke-WebRequest -Uri $dataSourcesUri -Method Get -WebSession $WebSession -UseDefaultCredentials -Verbose:$false + $response = Invoke-WebRequest -Uri $dataSourcesUri -Method Get -WebSession $WebSession -UseDefaultCredentials -UseBasicParsing -Verbose:$false } $itemWithDataSources = ConvertFrom-Json $response.Content @@ -114,4 +121,4 @@ function Get-RsRestItemDataSource throw (New-Object System.Exception("Failed to get data sources for '$RsItem': $($_.Exception.Message)", $_.Exception)) } } -} \ No newline at end of file +} diff --git a/ReportingServicesTools/Functions/CatalogItems/Rest/New-RsRestCacheRefreshPlan.ps1 b/ReportingServicesTools/Functions/CatalogItems/Rest/New-RsRestCacheRefreshPlan.ps1 new file mode 100644 index 00000000..4322cd4a --- /dev/null +++ b/ReportingServicesTools/Functions/CatalogItems/Rest/New-RsRestCacheRefreshPlan.ps1 @@ -0,0 +1,158 @@ +# Copyright (c) 2016 Microsoft Corporation. All Rights Reserved. +# Licensed under the MIT License (MIT) + +function New-RsRestCacheRefreshPlan +{ + <# + .SYNOPSIS + This function creates a new CacheRefreshPlan for the specified Power BI Report. + + .DESCRIPTION + This function creates a new CacheRefreshPlan for the specified Power BI Report. + + .PARAMETER RsItem + Specify the location of the Power BI Report. + + .PARAMETER ReportPortalUri + Specify the Report Portal URL to your Power BI Report Server Instance. + + .PARAMETER Recurrence + Specify the recurrence frequency + + .PARAMETER RestApiVersion + Specify the version of REST Endpoint to use. Valid values are: "v2.0". + + .PARAMETER Credential + Specify the credentials to use when connecting to the Report Server. + + .PARAMETER WebSession + Specify the session to be used when making calls to REST Endpoint. + + .PARAMETER Description + Specify the description to be added to the CacheRefreshPlan. + + .EXAMPLE + New-RsRestCacheRefreshPlan -RsItem /ReportCatalog + + Description + ----------- + Creates a new CacheRefreshPlan for the ReportCatalog Power BI Report under "/" parent folder. + + .EXAMPLE + New-RsRestCacheRefreshPlan -RsItem /ReportCatalog -Recurrence @{ + "DailyRecurrence" = @{ + "DaysInterval" = "1"; + } + } + + Description + ----------- + Creates a new CacheRefreshPlan for the ReportCatalog Power BI Report under "/" parent folder, with daily recurrence. + + .EXAMPLE + New-RsRestCacheRefreshPlan -RsItem /ReportCatalog -StartDateTime "2021-01-07T06:00:00-08:00" -Recurrence @{ + "DailyRecurrence" = @{ + "DaysInterval" = "1"; + } + } + + Description + ----------- + Creates a new CacheRefreshPlan for the ReportCatalog Power BI Report under "/" parent folder, which will reoccur daily at 6 AM. + #> + + [CmdletBinding()] + param( + [Parameter(Mandatory = $True)] + [string] + $RsItem, + + [Parameter(Mandatory = $False)] + [string] + $ReportPortalUri, + + [Parameter(Mandatory = $False)] + [string] + $Description, + + [Parameter(Mandatory = $False)] + $Recurrence = @{ + "DailyRecurrence" = @{ + "DaysInterval" = "1"; + } + }, + + [Parameter(Mandatory = $False)] + $StartDateTime = "2021-01-07T02:00:00-08:00", + + [Parameter(Mandatory = $False)] + $EndDate = "1901-02-01T00:00:00-08:00", + + [Alias('ApiVersion')] + [ValidateSet("v2.0")] + [string] + $RestApiVersion = "v2.0", + + [Alias('ReportServerCredentials')] + [System.Management.Automation.PSCredential] + $Credential, + + [Microsoft.PowerShell.Commands.WebRequestSession] + $WebSession + ) + Begin + { + $WebSession = New-RsRestSessionHelper -BoundParameters $PSBoundParameters + if ($null -ne $WebSession.Credentials -and $null -eq $Credential) { + Write-Verbose "Using credentials from WebSession" + $Credential = New-Object System.Management.Automation.PSCredential "$($WebSession.Credentials.UserName)@$($WebSession.Credentials.Domain)", $WebSession.Credentials.SecurePassword + } + $ReportPortalUri = Get-RsPortalUriHelper -WebSession $WebSession + $refreshplansUri = $ReportPortalUri + "api/$RestApiVersion/CacheRefreshPlans" + } + Process + { + try + { + if ((Get-RsRestItem -RsItem $RsItem -ReportPortalUri $ReportPortalUri -WebSession $WebSession -Credential $Credential -Verbose:$false).Type -ne 'PowerBIReport' ) + { + Write-Warning "Unable to create a CacheRefreshPlan for $RsItem because it is not a Power BI report." + } + else + { + $payload = @{ + "CatalogItemPath" = $RsItem; + "EventType" = "DataModelRefresh"; + "Schedule" = @{ + "Definition" = @{ + "StartDateTime" = $StartDateTime; + "EndDateSpecified" = $false; + "EndDate" = $EndDate; + "Recurrence" = $recurrence; + } + } + "Description" = $Description; + } + + $payloadJson = ConvertTo-Json $payload -Depth 15 + Write-Verbose "Payload: $payloadJson" + + if ($Credential -ne $null) + { + $response = Invoke-WebRequest -Uri $refreshplansUri -Method Post -WebSession $WebSession -Body ([System.Text.Encoding]::UTF8.GetBytes($payloadJson)) -ContentType "application/json" -Credential $Credential -UseBasicParsing -Verbose:$false + } + else + { + $response = Invoke-WebRequest -Uri $refreshplansUri -Method Post -WebSession $WebSession -Body ([System.Text.Encoding]::UTF8.GetBytes($payloadJson)) -ContentType "application/json" -UseDefaultCredentials -UseBasicParsing -Verbose:$false + } + + Write-Verbose "Schedule payload for $RsItem was created successfully!" + return $response + } + } + catch + { + throw (New-Object System.Exception("Failed to create CacheRefreshPlan $($_.Exception.Message)", $_.Exception)) + } + } +} diff --git a/ReportingServicesTools/Functions/CatalogItems/Rest/New-RsRestFolder.ps1 b/ReportingServicesTools/Functions/CatalogItems/Rest/New-RsRestFolder.ps1 index bec2a369..10b49670 100644 --- a/ReportingServicesTools/Functions/CatalogItems/Rest/New-RsRestFolder.ps1 +++ b/ReportingServicesTools/Functions/CatalogItems/Rest/New-RsRestFolder.ps1 @@ -29,7 +29,7 @@ function New-RsRestFolder Specify the session to be used when making calls to REST Endpoint. .EXAMPLE - New-RsRestFolder -RsFolder MyNewFolder -RsFolder / + New-RsRestFolder -FolderName MyNewFolder -RsFolder / Description ----------- @@ -65,6 +65,10 @@ function New-RsRestFolder Begin { $WebSession = New-RsRestSessionHelper -BoundParameters $PSBoundParameters + if ($null -ne $WebSession.Credentials -and $null -eq $Credential) { + Write-Verbose "Using credentials from WebSession" + $Credential = New-Object System.Management.Automation.PSCredential "$($WebSession.Credentials.UserName)@$($WebSession.Credentials.Domain)", $WebSession.Credentials.SecurePassword + } $ReportPortalUri = Get-RsPortalUriHelper -WebSession $WebSession $foldersUri = $ReportPortalUri + "api/$RestApiVersion/Folders" } @@ -91,11 +95,11 @@ function New-RsRestFolder if ($Credential -ne $null) { - $response = Invoke-WebRequest -Uri $foldersUri -Method Post -WebSession $WebSession -Body ([System.Text.Encoding]::UTF8.GetBytes($payloadJson)) -ContentType "application/json" -Credential $Credential -Verbose:$false + $response = Invoke-WebRequest -Uri $foldersUri -Method Post -WebSession $WebSession -Body ([System.Text.Encoding]::UTF8.GetBytes($payloadJson)) -ContentType "application/json" -Credential $Credential -UseBasicParsing -Verbose:$false } else { - $response = Invoke-WebRequest -Uri $foldersUri -Method Post -WebSession $WebSession -Body ([System.Text.Encoding]::UTF8.GetBytes($payloadJson)) -ContentType "application/json" -UseDefaultCredentials -Verbose:$false + $response = Invoke-WebRequest -Uri $foldersUri -Method Post -WebSession $WebSession -Body ([System.Text.Encoding]::UTF8.GetBytes($payloadJson)) -ContentType "application/json" -UseDefaultCredentials -UseBasicParsing -Verbose:$false } Write-Verbose "Folder $TargetFolderPath was created successfully!" @@ -106,4 +110,4 @@ function New-RsRestFolder throw (New-Object System.Exception("Failed to create folder '$FolderName' in '$RsFolder': $($_.Exception.Message)", $_.Exception)) } } -} \ No newline at end of file +} diff --git a/ReportingServicesTools/Functions/CatalogItems/Rest/Out-RsRestCatalogItem.ps1 b/ReportingServicesTools/Functions/CatalogItems/Rest/Out-RsRestCatalogItem.ps1 index b20a0cad..0692fb56 100644 --- a/ReportingServicesTools/Functions/CatalogItems/Rest/Out-RsRestCatalogItem.ps1 +++ b/ReportingServicesTools/Functions/CatalogItems/Rest/Out-RsRestCatalogItem.ps1 @@ -33,14 +33,14 @@ function Out-RsRestCatalogItem .EXAMPLE Out-RsRestCatalogItem -RsItem '/Report' -Destination 'C:\reports' - + Description ----------- Downloads the catalog item 'Report' to folder 'C:\reports' using v2.0 REST Endpoint from the Report Server located at http://localhost/reports. .EXAMPLE Out-RsRestCatalogItem -RsItem '/Report' -Destination 'C:\reports' -RestApiVersion 'v1.0' - + Description ----------- Downloads the catalog item 'Report' to folder 'C:\reports' using v1.0 REST Endpoint from the Report Server located at http://localhost/reports. @@ -54,7 +54,7 @@ function Out-RsRestCatalogItem .EXAMPLE Out-RsRestCatalogItem -ReportPortalUri 'http://myserver/reports' -RsItem '/Report' -Destination 'C:\reports' - + Description ----------- Downloads the catalog item found at '/Report' to folder 'C:\reports' using v2.0 REST Endpoint from the Report Server located at http://myserver/reports. @@ -67,7 +67,7 @@ function Out-RsRestCatalogItem [string[]] $RsItem, - [ValidateScript({ Test-Path $_ -PathType Container})] + [ValidateScript({Test-Path -LiteralPath $_ -PathType Container})] [Parameter(Mandatory = $True)] [string] $Destination, @@ -94,6 +94,10 @@ function Out-RsRestCatalogItem Begin { $WebSession = New-RsRestSessionHelper -BoundParameters $PSBoundParameters + if ($null -ne $WebSession.Credentials -and $null -eq $Credential) { + Write-Verbose "Using credentials from WebSession" + $Credential = New-Object System.Management.Automation.PSCredential "$($WebSession.Credentials.UserName)@$($WebSession.Credentials.Domain)", $WebSession.Credentials.SecurePassword + } $ReportPortalUri = Get-RsPortalUriHelper -WebSession $WebSession if ($RestApiVersion -eq 'v1.0') { @@ -114,11 +118,11 @@ function Out-RsRestCatalogItem $url = [string]::Format($catalogItemsByPathApi, $item) if ($Credential -ne $null) { - $response = Invoke-WebRequest -Uri $url -Method Get -Credential $Credential -Verbose:$false + $response = Invoke-WebRequest -Uri $url -Method Get -Credential $Credential -UseBasicParsing -Verbose:$false } else { - $response = Invoke-WebRequest -Uri $url -Method Get -UseDefaultCredentials -Verbose:$false + $response = Invoke-WebRequest -Uri $url -Method Get -UseDefaultCredentials -UseBasicParsing -Verbose:$false } } catch @@ -132,4 +136,4 @@ function Out-RsRestCatalogItem Out-RsRestCatalogItemId -RsItemInfo $itemInfo -Destination $Destination -RestApiVersion $RestApiVersion -ReportPortalUri $ReportPortalUri -Credential $Credential -WebSession $WebSession -Overwrite:$Overwrite } } -} \ No newline at end of file +} diff --git a/ReportingServicesTools/Functions/CatalogItems/Rest/Out-RsRestCatalogItemId.ps1 b/ReportingServicesTools/Functions/CatalogItems/Rest/Out-RsRestCatalogItemId.ps1 index 00ae5fec..54285ce3 100644 --- a/ReportingServicesTools/Functions/CatalogItems/Rest/Out-RsRestCatalogItemId.ps1 +++ b/ReportingServicesTools/Functions/CatalogItems/Rest/Out-RsRestCatalogItemId.ps1 @@ -37,7 +37,7 @@ function Out-RsRestCatalogItemId [Parameter(Mandatory = $True)] $RsItemInfo, - [ValidateScript({ Test-Path $_ -PathType Container})] + [ValidateScript({Test-Path -LiteralPath $_ -PathType Container})] [Parameter(Mandatory = $True)] [string] $Destination, @@ -64,9 +64,13 @@ function Out-RsRestCatalogItemId Begin { $WebSession = New-RsRestSessionHelper -BoundParameters $PSBoundParameters + if ($null -ne $WebSession.Credentials -and $null -eq $Credential) { + Write-Verbose "Using credentials from WebSession" + $Credential = New-Object System.Management.Automation.PSCredential "$($WebSession.Credentials.UserName)@$($WebSession.Credentials.Domain)", $WebSession.Credentials.SecurePassword + } $ReportPortalUri = Get-RsPortalUriHelper -WebSession $WebSession $catalogItemContentApi = $ReportPortalUri + "api/$RestApiVersion/CatalogItems({0})/Content/`$value" - $DestinationFullPath = Convert-Path $Destination + $DestinationFullPath = Convert-Path -LiteralPath $Destination # basic validation of RsItemInfo by checking properties that would be defined on a valid RsItemInfo object if ($RsItemInfo.Id -eq $null -or @@ -79,6 +83,13 @@ function Out-RsRestCatalogItemId Process { + #exlude Linked Reports + if ($RsItemInfo.Type -eq 'LinkedReport') + { + Write-Verbose "$($RsItemInfo.Path) is a LinkedReport and is ignored." + return + } + if ($RsItemInfo.Type -ne 'MobileReport') { $itemId = $RsItemInfo.Id @@ -128,11 +139,11 @@ function Out-RsRestCatalogItemId $url = [string]::Format($catalogItemContentApi, $itemId) if ($Credential -ne $null) { - $response = Invoke-WebRequest -Uri $url -Method Get -Credential $Credential -Verbose:$false + $response = Invoke-WebRequest -Uri $url -Method Get -Credential $Credential -UseBasicParsing -Verbose:$false } else { - $response = Invoke-WebRequest -Uri $url -Method Get -UseDefaultCredentials -Verbose:$false + $response = Invoke-WebRequest -Uri $url -Method Get -UseDefaultCredentials -UseBasicParsing -Verbose:$false } } catch @@ -145,4 +156,3 @@ function Out-RsRestCatalogItemId Write-Verbose "$($RsItemInfo.Path) was downloaded to $destinationFilePath successfully!" } } - diff --git a/ReportingServicesTools/Functions/CatalogItems/Rest/Out-RsRestFolderContent.ps1 b/ReportingServicesTools/Functions/CatalogItems/Rest/Out-RsRestFolderContent.ps1 index 0a72a34d..3cb02188 100644 --- a/ReportingServicesTools/Functions/CatalogItems/Rest/Out-RsRestFolderContent.ps1 +++ b/ReportingServicesTools/Functions/CatalogItems/Rest/Out-RsRestFolderContent.ps1 @@ -33,28 +33,28 @@ function Out-RsRestFolderContent .EXAMPLE Out-RsRestFolderContent -RsFolder /folder -Destination 'C:\reports' - + Description ----------- Downloads all items found under '/folder' folder to 'C:\reports' using v2.0 REST Endpoint from the Report Server located at http://localhost/reports. .EXAMPLE Out-RsRestFolderContent -RsFolder '/folder' -Destination 'C:\reports' -RestApiVersion 'v1.0' - + Description ----------- Downloads all items found under '/folder' folder to 'C:\reports' using v1.0 REST Endpoint from the Report Server located at http://localhost/reports. .EXAMPLE Out-RsRestFolderContent -WebSession $mySession -RsFolder '/folder' -Destination 'C:\reports' - + Description ----------- Downloads all items found under '/folder' folder to 'C:\reports' using v2.0 REST Endpoint from the Report Server located at specified WebSession object. .EXAMPLE Out-RsRestFolderContent -ReportPortalUri 'http://myserver/reports' -RsFolder '/folder' -Destination 'C:\reports' - + Description ----------- Downloads all items found under '/folder' folder to 'C:\reports' using v2.0 REST Endpoint from the Report Server located at http://myserver/reports. @@ -66,7 +66,7 @@ function Out-RsRestFolderContent [string] $RsFolder, - [ValidateScript({ Test-Path $_ -PathType Container})] + [ValidateScript({Test-Path -LiteralPath $_ -PathType Container})] [Parameter(Mandatory = $True)] [string] $Destination, @@ -93,6 +93,10 @@ function Out-RsRestFolderContent Begin { $WebSession = New-RsRestSessionHelper -BoundParameters $PSBoundParameters + if ($null -ne $WebSession.Credentials -and $null -eq $Credential) { + Write-Verbose "Using credentials from WebSession" + $Credential = New-Object System.Management.Automation.PSCredential "$($WebSession.Credentials.UserName)@$($WebSession.Credentials.Domain)", $WebSession.Credentials.SecurePassword + } $ReportPortalUri = Get-RsPortalUriHelper -WebSession $WebSession $catalogItemsByPathApiV1 = $ReportPortalUri + "api/v1.0/CatalogItemByPath(path=@path)?@path=%27{0}%27" $folderCatalogItemsApiV1 = $ReportPortalUri + "api/v1.0/CatalogItems({0})/Model.Folder/CatalogItems" @@ -108,11 +112,11 @@ function Out-RsRestFolderContent $url = [string]::Format($catalogItemsByPathApiV1, $RsFolder) if ($Credential -ne $null) { - $response = Invoke-WebRequest -Uri $url -Method Get -Credential $Credential -Verbose:$false + $response = Invoke-WebRequest -Uri $url -Method Get -Credential $Credential -UseBasicParsing -Verbose:$false } else { - $response = Invoke-WebRequest -Uri $url -Method Get -UseDefaultCredentials -Verbose:$false + $response = Invoke-WebRequest -Uri $url -Method Get -UseDefaultCredentials -UseBasicParsing -Verbose:$false } } catch @@ -128,11 +132,11 @@ function Out-RsRestFolderContent $url = [string]::Format($folderCatalogItemsApiV1, $folder.Id) if ($Credential -ne $null) { - $response = Invoke-WebRequest -Uri $url -Method Get -Credential $Credential -Verbose:$false + $response = Invoke-WebRequest -Uri $url -Method Get -Credential $Credential -UseBasicParsing -Verbose:$false } else { - $response = Invoke-WebRequest -Uri $url -Method Get -UseDefaultCredentials -Verbose:$false + $response = Invoke-WebRequest -Uri $url -Method Get -UseDefaultCredentials -UseBasicParsing -Verbose:$false } } catch @@ -148,11 +152,11 @@ function Out-RsRestFolderContent $url = [string]::Format($folderCatalogItemsApiLatest, $RsFolder) if ($Credential -ne $null) { - $response = Invoke-WebRequest -Uri $url -Method Get -Credential $Credential -Verbose:$false + $response = Invoke-WebRequest -Uri $url -Method Get -Credential $Credential -UseBasicParsing -Verbose:$false } else { - $response = Invoke-WebRequest -Uri $url -Method Get -UseDefaultCredentials -Verbose:$false + $response = Invoke-WebRequest -Uri $url -Method Get -UseDefaultCredentials -UseBasicParsing -Verbose:$false } } catch diff --git a/ReportingServicesTools/Functions/CatalogItems/Rest/Remove-RsRestCacheRefreshPlan.ps1 b/ReportingServicesTools/Functions/CatalogItems/Rest/Remove-RsRestCacheRefreshPlan.ps1 new file mode 100644 index 00000000..6d67f11b --- /dev/null +++ b/ReportingServicesTools/Functions/CatalogItems/Rest/Remove-RsRestCacheRefreshPlan.ps1 @@ -0,0 +1,129 @@ +# Copyright (c) 2020 Microsoft Corporation. All Rights Reserved. +# Licensed under the MIT License (MIT) + +function Remove-RsRestCacheRefreshPlan { + <# + .SYNOPSIS + This function deletes a CacheRefreshPlan from a report from the Report Server. + .DESCRIPTION + This function deletes a CacheRefreshPlan from a report from the Report Server using + the REST API. Alternatively, when a report has multiple CacheRefreshPlans you can specify which + CacheRefreshPlan to delete by passing the Id of the CacheRefreshPlan to the -Id parameter. + .PARAMETER RsReport + Specify the location of the report which should have its CacheRefreshPlans deleted. + .PARAMETER Id + Specify the Id of the CacheRefreshPlan to delete. + .PARAMETER ReportPortalUri + Specify the Report Portal URL to your SQL Server Reporting Services Instance or Power BI Report Server Instance. + .PARAMETER RestApiVersion + Specify the version of REST Endpoint to use. Valid values are: "v2.0". + .PARAMETER Credential + Specify the credentials to use when connecting to the Report Server. + .PARAMETER WebSession + Specify the session to be used when making calls to REST Endpoint. + + .EXAMPLE + Remove-RsCacheRefreshPlan -RsReport '/MyReport' + + Description + ----------- + Fetches the CacheRefreshPlan of a report named "MyReport" found in "/" folder from the Report Server located at http://localhost/reports, + and deletes it. + NOTE: This only works when the report has a single CacheRefreshPlan. + + .EXAMPLE + $scheduleID = (Get-RsCacheRefreshPlan -RsReport '/MyReport').ID[0] + Remove-RsCacheRefreshPlan -ID $scheduleID + + Description + ----------- + Fetches the CacheRefreshPlan of a report named "MyReport" found in "/" folder from the Report Server and gets the ID of the first Schedule + refresh (when multiple are present), then deletes it. + #> + + [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')] + param( + [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true)] + [Alias('ItemPath','Path', 'RsItem')] + [string] + $RsReport, + + [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true)] + [Alias('CacheRefreshPlan')] + [string] + $Id = $null, + + [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true)] + [string] + $ReportPortalUri, + + [Alias('ApiVersion')] + [ValidateSet("v2.0")] + [string] + $RestApiVersion = "v2.0", + + [Alias('ReportServerCredentials')] + [System.Management.Automation.PSCredential] + $Credential, + + [Microsoft.PowerShell.Commands.WebRequestSession] + $WebSession + ) + Begin + { + $WebSession = New-RsRestSessionHelper -BoundParameters $PSBoundParameters + if ($null -ne $WebSession.Credentials -and $null -eq $Credential) { + Write-Verbose "Using credentials from WebSession" + $Credential = New-Object System.Management.Automation.PSCredential "$($WebSession.Credentials.UserName)@$($WebSession.Credentials.Domain)", $WebSession.Credentials.SecurePassword + } + $ReportPortalUri = Get-RsPortalUriHelper -WebSession $WebSession + $CacheRefreshPlansUri = $ReportPortalUri + "api/$RestApiVersion/CacheRefreshPlans({0})" + } + Process + { + try + { + if (-not $RsReport) + { + Write-Verbose "Fetching CacheRefreshPlans for Id $Id..." + $CacheRefreshPlansUri = [String]::Format($CacheRefreshPlansUri, $Id) + } + else + { + Write-Verbose "Fetching CacheRefreshPlans for $RsReport..." + if ($Credential -ne $null) + { + $RefreshPlan = Get-RsCacheRefreshPlan -ReportPortalUri $ReportPortalUri -RsReport $RsReport -WebSession $WebSession -Credential $Credential -Verbose:$false + } + else + { + $RefreshPlan = Get-RsCacheRefreshPlan -ReportPortalUri $ReportPortalUri -RsReport $RsReport -WebSession $WebSession -Verbose:$false + } + if ($RefreshPlan.Count -le 1) + { + $CacheRefreshPlansUri = [String]::Format($CacheRefreshPlansUri, $RefreshPlan.Id) + } + else + { + Write-Warning "Unable to delete scheduled refresh for $RsReport because multiple CacheRefreshPlans are present." + } + } + Write-Verbose "$($CacheRefreshPlansUri)" + + Write-Verbose "Deleting $($RefreshPlan.RsReport)$($Id)..." + if ($Credential -ne $null) + { + $response = Invoke-RestMethod -Uri $CacheRefreshPlansUri -Method Delete -WebSession $WebSession -Credential $Credential -Verbose:$false + } + else + { + $response = Invoke-RestMethod -Uri $CacheRefreshPlansUri -Method Delete -WebSession $WebSession -UseDefaultCredentials -Verbose:$false + } + } + catch + { + throw (New-Object System.Exception("Unable to delete '$($RefreshPlan.RsReport)' '$($Id)': $($_.Exception.Message)", $_.Exception)) + } + } +} +New-Alias -Name "Remove-RsPbiReportRefresh" -Value Remove-RsRestCacheRefreshPlan -Scope Global \ No newline at end of file diff --git a/ReportingServicesTools/Functions/CatalogItems/Rest/Remove-RsRestCatalogItem.ps1 b/ReportingServicesTools/Functions/CatalogItems/Rest/Remove-RsRestCatalogItem.ps1 index 6487f685..5e80436b 100644 --- a/ReportingServicesTools/Functions/CatalogItems/Rest/Remove-RsRestCatalogItem.ps1 +++ b/ReportingServicesTools/Functions/CatalogItems/Rest/Remove-RsRestCatalogItem.ps1 @@ -57,6 +57,10 @@ function Remove-RsRestCatalogItem Begin { $WebSession = New-RsRestSessionHelper -BoundParameters $PSBoundParameters + if ($null -ne $WebSession.Credentials -and $null -eq $Credential) { + Write-Verbose "Using credentials from WebSession" + $Credential = New-Object System.Management.Automation.PSCredential "$($WebSession.Credentials.UserName)@$($WebSession.Credentials.Domain)", $WebSession.Credentials.SecurePassword + } $ReportPortalUri = Get-RsPortalUriHelper -WebSession $WebSession $catalogItemsUri = $ReportPortalUri + "api/$RestApiVersion/CatalogItems(Path='{0}')" } @@ -76,11 +80,11 @@ function Remove-RsRestCatalogItem if ($Credential -ne $null) { - Invoke-WebRequest -Uri $catalogItemsUri -Method Delete -WebSession $WebSession -Credential $Credential -Verbose:$false | Out-Null + Invoke-WebRequest -Uri $catalogItemsUri -Method Delete -WebSession $WebSession -Credential $Credential -UseBasicParsing -Verbose:$false | Out-Null } else { - Invoke-WebRequest -Uri $catalogItemsUri -Method Delete -WebSession $WebSession -UseDefaultCredentials -Verbose:$false | Out-Null + Invoke-WebRequest -Uri $catalogItemsUri -Method Delete -WebSession $WebSession -UseDefaultCredentials -UseBasicParsing -Verbose:$false | Out-Null } Write-Verbose "Catalog item $RsItem was deleted successfully!" @@ -91,4 +95,4 @@ function Remove-RsRestCatalogItem } } } -} \ No newline at end of file +} diff --git a/ReportingServicesTools/Functions/CatalogItems/Rest/Remove-RsRestFolder.ps1 b/ReportingServicesTools/Functions/CatalogItems/Rest/Remove-RsRestFolder.ps1 index 47669a42..0f266e99 100644 --- a/ReportingServicesTools/Functions/CatalogItems/Rest/Remove-RsRestFolder.ps1 +++ b/ReportingServicesTools/Functions/CatalogItems/Rest/Remove-RsRestFolder.ps1 @@ -57,6 +57,10 @@ function Remove-RsRestFolder Begin { $WebSession = New-RsRestSessionHelper -BoundParameters $PSBoundParameters + if ($null -ne $WebSession.Credentials -and $null -eq $Credential) { + Write-Verbose "Using credentials from WebSession" + $Credential = New-Object System.Management.Automation.PSCredential "$($WebSession.Credentials.UserName)@$($WebSession.Credentials.Domain)", $WebSession.Credentials.SecurePassword + } $ReportPortalUri = Get-RsPortalUriHelper -WebSession $WebSession $foldersUri = $ReportPortalUri + "api/$RestApiVersion/Folders(Path='{0}')" } diff --git a/ReportingServicesTools/Functions/CatalogItems/Rest/Set-RsRestItemDataModelParameters.ps1 b/ReportingServicesTools/Functions/CatalogItems/Rest/Set-RsRestItemDataModelParameters.ps1 new file mode 100644 index 00000000..5f8d9f3e --- /dev/null +++ b/ReportingServicesTools/Functions/CatalogItems/Rest/Set-RsRestItemDataModelParameters.ps1 @@ -0,0 +1,114 @@ +# Copyright (c) 2017 Microsoft Corporation. All Rights Reserved. +# Licensed under the MIT License (MIT) + +function Set-RsRestItemDataModelParameter +{ + <# + .SYNOPSIS + This function updates data sources related to a catalog item from the Report Server. + + .DESCRIPTION + This function updates data sources related to a catalog item from the Report Server. This is currently only applicable to Power BI Reports and only from ReportServer October/2020 or higher. + + .PARAMETER RsItem + Specify the location of the catalog item whose data sources will be updated. + + .PARAMETER DataModelParameters + Specify the data model parameters which were initially fetched via Get-RsRestItemDataModelParameters. + + .PARAMETER ReportPortalUri + Specify the Report Portal URL to your SQL Server Reporting Services Instance. + + .PARAMETER RestApiVersion + Specify the version of REST Endpoint to use. Valid values are: "v2.0". + + .PARAMETER Credential + Specify the credentials to use when connecting to the Report Server. + + .PARAMETER WebSession + Specify the session to be used when making calls to REST Endpoint. + + .EXAMPLE + $parameters = Get-RsRestItemDataModelParameters -RsItem '/MyPowerBIReport' + $parameters[0].Value = 'NewValue' + Set-RsRestItemDataModelParameter -RsItem '/MyPowerBIReport' -DataModelParameters $parameters + + Description + ----------- + Updates data model parameters to the specified $parameters object. This example is only applicable for Power BI Reports. + + .LINK + https://docs.microsoft.com/en-us/power-bi/report-server/connect-data-source-apis + #> + + [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')] + param( + [Parameter(Mandatory = $True)] + [string] + $RsItem, + + [Parameter(Mandatory = $True)] + $DataModelParameters, + + [string] + $ReportPortalUri, + + [ValidateSet("v2.0")] + [string] + $RestApiVersion = "v2.0", + + [System.Management.Automation.PSCredential] + $Credential, + + [Microsoft.PowerShell.Commands.WebRequestSession] + $WebSession + ) + Begin + { + $WebSession = New-RsRestSessionHelper -BoundParameters $PSBoundParameters + if ($null -ne $WebSession.Credentials -and $null -eq $Credential) { + Write-Verbose "Using credentials from WebSession" + $Credential = New-Object System.Management.Automation.PSCredential "$($WebSession.Credentials.UserName)@$($WebSession.Credentials.Domain)", $WebSession.Credentials.SecurePassword + } + $ReportPortalUri = Get-RsPortalUriHelper -WebSession $WebSession + $dataModelParametersUri = $ReportPortalUri + "api/$RestApiVersion/PowerBIReports(Path='{0}')/DataModelParameters" + } + Process + { + try + { + $dataModelParametersUri = [String]::Format($dataModelParametersUri, $RsItem) + + # Converting $DataModelParameters into array as PowerBIReport(...)/DataModelParameters expects data model parameters + # to be in an array in the request body. If $DataModelParameters is already an array, this operation + # combines $DataModelParameters array with an empty array, so result is still an array. + $dataModelParametersArray = @($DataModelParameters) + + $payloadJson = ConvertTo-Json -InputObject $dataModelParametersArray -Depth 3 + + Write-Verbose "Payload for parameters: $($payloadJson)" + + $method = "POST" + + if ($PSCmdlet.ShouldProcess($RsItem, "Update data model parameters")) + { + Write-Verbose "Updating data model parameters for $($RsItem)..." + if ($Credential -ne $null) + { + Invoke-WebRequest -Uri $dataModelParametersUri -Method $method -Body ([System.Text.Encoding]::UTF8.GetBytes($payloadJson)) -ContentType "application/json" -WebSession $WebSession -Credential $Credential -UseBasicParsing -Verbose:$false | Out-Null + } + else + { + Invoke-WebRequest -Uri $dataModelParametersUri -Method $method -Body ([System.Text.Encoding]::UTF8.GetBytes($payloadJson)) -ContentType "application/json" -WebSession $WebSession -UseDefaultCredentials -UseBasicParsing -Verbose:$false | Out-Null + } + Write-Verbose "Data model parameters were updated successfully!" + } + } + catch + { + Write-Error "Error updating data model parameters for for '$RsItem': $($_.Exception.Message)" + throw (New-Object System.Exception("Failed to update data model parameters for '$RsItem': $($_.Exception.Message)", $_.Exception)) + } + } +} +New-Alias -Name "Set-RsRestItemDataModelParameters" -Value Set-RsRestItemDataModelParameter -Scope Global diff --git a/ReportingServicesTools/Functions/CatalogItems/Rest/Set-RsRestItemDataSource.ps1 b/ReportingServicesTools/Functions/CatalogItems/Rest/Set-RsRestItemDataSource.ps1 index e80a1d0d..83f4fa15 100644 --- a/ReportingServicesTools/Functions/CatalogItems/Rest/Set-RsRestItemDataSource.ps1 +++ b/ReportingServicesTools/Functions/CatalogItems/Rest/Set-RsRestItemDataSource.ps1 @@ -100,6 +100,9 @@ function Set-RsRestItemDataSource Description ----------- Updates data sources to the specified $dataSources object. This example is only applicable to Paginated Reports. + + .LINK + https://docs.microsoft.com/en-us/power-bi/report-server/connect-data-source-apis-pre-oct-2020 #> [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')] @@ -135,6 +138,10 @@ function Set-RsRestItemDataSource Begin { $WebSession = New-RsRestSessionHelper -BoundParameters $PSBoundParameters + if ($null -ne $WebSession.Credentials -and $null -eq $Credential) { + Write-Verbose "Using credentials from WebSession" + $Credential = New-Object System.Management.Automation.PSCredential "$($WebSession.Credentials.UserName)@$($WebSession.Credentials.Domain)", $WebSession.Credentials.SecurePassword + } $ReportPortalUri = Get-RsPortalUriHelper -WebSession $WebSession $dataSourcesUri = $ReportPortalUri + "api/$RestApiVersion/{0}(Path='{1}')/DataSources" } @@ -170,33 +177,35 @@ function Set-RsRestItemDataSource } elseif ($ds.DataSourceSubType -eq $null) { - # DataSourceType, ConnectionString and CredentialRetrieval must always be specified! - if ($ds.DataSourceType -eq $null -or - $ds.ConnectionString -eq $null -or - $ds.CredentialRetrieval -eq $null -or - !($ds.CredentialRetrieval -LIKE 'Integrated' -or - $ds.CredentialRetrieval -LIKE 'Store' -or - $ds.CredentialRetrieval -LIKE 'Prompt' -or - $ds.CredentialRetrieval -LIKE 'None')) - { - throw "Invalid data source specified: $ds!" - } - elseif ($ds.DataModelDataSource -ne $null) - { - # since this is an embedded data source for Paginated Report/Shared data set, - # you should not set any value to DataModelDataSource - throw "You cannot specify DataModelDataSource for this datasource: $ds!" - } - - if ($ds.CredentialRetrieval -LIKE 'Store' -and $ds.CredentialsInServer -eq $null) - { - # CredentialsInServer must be specified for Store - throw "CredentialsInServer must be specified when CredentialRetrieval is set to Store: $ds!" - } - elseif ($ds.CredentialRetrieval -LIKE 'Prompt' -and $ds.CredentialsByUser -eq $null) - { - # CredentialsByUser must be specified for Prompt - throw "CredentialsByUser must be specified when CredentialRetrieval is set to Prompt: $ds!" + if (!$ds.IsReference) { + # DataSourceType, ConnectionString and CredentialRetrieval must always be specified! + if ($ds.DataSourceType -eq $null -or + $ds.ConnectionString -eq $null -or + $ds.CredentialRetrieval -eq $null -or + !($ds.CredentialRetrieval -LIKE 'Integrated' -or + $ds.CredentialRetrieval -LIKE 'Store' -or + $ds.CredentialRetrieval -LIKE 'Prompt' -or + $ds.CredentialRetrieval -LIKE 'None')) + { + throw "Invalid data source specified: $ds!" + } + elseif ($ds.DataModelDataSource -ne $null) + { + # since this is an embedded data source for Paginated Report/Shared data set, + # you should not set any value to DataModelDataSource + throw "You cannot specify DataModelDataSource for this datasource: $ds!" + } + + if ($ds.CredentialRetrieval -LIKE 'Store' -and $ds.CredentialsInServer -eq $null) + { + # CredentialsInServer must be specified for Store + throw "CredentialsInServer must be specified when CredentialRetrieval is set to Store: $ds!" + } + elseif ($ds.CredentialRetrieval -LIKE 'Prompt' -and $ds.CredentialsByUser -eq $null) + { + # CredentialsByUser must be specified for Prompt + throw "CredentialsByUser must be specified when CredentialRetrieval is set to Prompt: $ds!" + } } } else @@ -235,11 +244,11 @@ function Set-RsRestItemDataSource Write-Verbose "Updating data sources for $($RsItem)..." if ($Credential -ne $null) { - Invoke-WebRequest -Uri $dataSourcesUri -Method $method -Body ([System.Text.Encoding]::UTF8.GetBytes($payloadJson)) -ContentType "application/json" -WebSession $WebSession -Credential $Credential -Verbose:$false | Out-Null + Invoke-WebRequest -Uri $dataSourcesUri -Method $method -Body ([System.Text.Encoding]::UTF8.GetBytes($payloadJson)) -ContentType "application/json" -WebSession $WebSession -Credential $Credential -UseBasicParsing -Verbose:$false | Out-Null } else { - Invoke-WebRequest -Uri $dataSourcesUri -Method $method -Body ([System.Text.Encoding]::UTF8.GetBytes($payloadJson)) -ContentType "application/json" -WebSession $WebSession -UseDefaultCredentials -Verbose:$false | Out-Null + Invoke-WebRequest -Uri $dataSourcesUri -Method $method -Body ([System.Text.Encoding]::UTF8.GetBytes($payloadJson)) -ContentType "application/json" -WebSession $WebSession -UseDefaultCredentials -UseBasicParsing -Verbose:$false | Out-Null } Write-Verbose "Data sources were updated successfully!" } @@ -249,4 +258,4 @@ function Set-RsRestItemDataSource throw (New-Object System.Exception("Failed to update data sources for '$RsItem': $($_.Exception.Message)", $_.Exception)) } } -} \ No newline at end of file +} diff --git a/ReportingServicesTools/Functions/CatalogItems/Rest/Start-RsRestCacheRefreshPlan.ps1 b/ReportingServicesTools/Functions/CatalogItems/Rest/Start-RsRestCacheRefreshPlan.ps1 new file mode 100644 index 00000000..1e37a1d2 --- /dev/null +++ b/ReportingServicesTools/Functions/CatalogItems/Rest/Start-RsRestCacheRefreshPlan.ps1 @@ -0,0 +1,152 @@ +# Copyright (c) 2020 Microsoft Corporation. All Rights Reserved. +# Licensed under the MIT License (MIT) + +function Start-RsRestCacheRefreshPlan +{ + <# + .SYNOPSIS + This function fetches the CacheRefreshPlan of a report from the Report Server, and refreshes them. + .DESCRIPTION + This function fetches the CacheRefreshPlan of a report from the Report Server, and refreshes them using + the REST API. Alternatively, when a report has multiple CacheRefreshPlans you can sopecify which + CacheRefreshPlan to refresh by passing the Id of the CacheRefreshPlan to the -Id parameter. + .PARAMETER RsReport + Specify the location of the report which should have its CacheRefreshPlans fetched & refreshed. + .PARAMETER Id + Specify the Id of the CacheRefreshPlan to start ferfreshing. + .PARAMETER ReportPortalUri + Specify the Report Portal URL to your SQL Server Reporting Services Instance or Power BI Report Server Instance. + .PARAMETER RestApiVersion + Specify the version of REST Endpoint to use. Valid values are: "v2.0". + .PARAMETER Credential + Specify the credentials to use when connecting to the Report Server. + .PARAMETER WebSession + Specify the session to be used when making calls to REST Endpoint. + .EXAMPLE + Start-RsReportRefresh -RsReport "/MyReport" + + Description + ----------- + Fetches the CacheRefreshPlans of a report named "MyReport" found in "/" folder from the Report Server located at http://localhost/reports, + and refreshes them. + + .EXAMPLE + Get-RsCacheRefreshPlan -RsReport '/MyReport' | Start-RsRestCacheRefreshPlan + + Description + ----------- + Fetches the CacheRefreshPlan of a report named "MyReport" found in "/" folder from the Report Server located at http://localhost/reports, + and refreshes them. + NOTE: This only works when the report has a single CacheRefreshPlan. + + .EXAMPLE + Start-RsRestCacheRefreshPlan -RsReport "/MyReport" -WebSession $session + + Description + ----------- + Fetches the CacheRefreshPlans of a report named "MyReport" catalog item found in "/" folder from the Report Server located at specificed WebSession object, + and refreshes them. + + .EXAMPLE + Start-RsRestCacheRefreshPlan -RsReport "/MyReport" -ReportPortalUri http://myserver/reports + + Description + ----------- + Fetches the CacheRefreshPlans of a report named "MyReport" found in "/" folder from the Report Server located at http://myserver/reports, and refreshes them. + + .EXAMPLE + $Sub = Get-RsSubscription -RsItem "/MyReport" + Start-RsRestCacheRefreshPlan -Id $Sub.Id -ReportPortalUri http://myserver/reports + + Description + ----------- + Fetches the Subscription of a paginated report named "MyReport" found in "/" folder from the Report Server located at http://myserver/reports, + and then pases the Id of the subscription to the Start-RsRestCacheRefreshPlan which starts the refresh. + #> + + [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')] + param( + [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true)] + [Alias('ItemPath','Path', 'RsItem')] + [string] + $RsReport, + + [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true)] + [Alias('CacheRefreshPlan')] + [string] + $Id = $null, + + [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true)] + [string] + $ReportPortalUri, + + [Alias('ApiVersion')] + [ValidateSet("v2.0")] + [string] + $RestApiVersion = "v2.0", + + [Alias('ReportServerCredentials')] + [System.Management.Automation.PSCredential] + $Credential, + + [Microsoft.PowerShell.Commands.WebRequestSession] + $WebSession + ) + Begin + { + $WebSession = New-RsRestSessionHelper -BoundParameters $PSBoundParameters + if ($null -ne $WebSession.Credentials -and $null -eq $Credential) { + Write-Verbose "Using credentials from WebSession" + $Credential = New-Object System.Management.Automation.PSCredential "$($WebSession.Credentials.UserName)@$($WebSession.Credentials.Domain)", $WebSession.Credentials.SecurePassword + } + $ReportPortalUri = Get-RsPortalUriHelper -WebSession $WebSession + $CacheRefreshPlansUri = $ReportPortalUri + "api/$RestApiVersion/CacheRefreshPlans({0})/Model.Execute" + } + Process + { + try + { + if (-not $RsReport) + { + Write-Verbose "Fetching CacheRefreshPlans for Id $Id..." + $CacheRefreshPlansUri = [String]::Format($CacheRefreshPlansUri, $Id) + } + else + { + Write-Verbose "Fetching CacheRefreshPlans for $RsReport..." + if ($Credential -ne $null) + { + $RefreshPlan = Get-RsCacheRefreshPlan -ReportPortalUri $ReportPortalUri -RsReport $RsReport -WebSession $WebSession -Credential $Credential -Verbose:$false + } + else + { + $RefreshPlan = Get-RsCacheRefreshPlan -ReportPortalUri $ReportPortalUri -RsReport $RsReport -WebSession $WebSession -Verbose:$false + } + if ($RefreshPlan.Count -le 1) + { + $CacheRefreshPlansUri = [String]::Format($CacheRefreshPlansUri, $RefreshPlan.Id) + } + else + { + Write-Warning "Unable to start a refresh for $RsReport because multiple CacheRefreshPlans are present." + } + } + Write-Verbose "$($CacheRefreshPlansUri)" + + Write-Verbose "Starting Refresh for $($RefreshPlan.RsReport)$($Id)..." + if ($Credential -ne $null) + { + $response = Invoke-RestMethod -Uri $CacheRefreshPlansUri -Method Post -WebSession $WebSession -Credential $Credential -Verbose:$false + } + else + { + $response = Invoke-RestMethod -Uri $CacheRefreshPlansUri -Method Post -WebSession $WebSession -UseDefaultCredentials -Verbose:$false + } + } + catch + { + throw (New-Object System.Exception("Unable to refresh model for '$($RefreshPlan.RsReport)' '$($Id)': $($_.Exception.Message)", $_.Exception)) + } + } +} +New-Alias -Name "Start-RsPbiReportRefresh" -Value Start-RsRestCacheRefreshPlan -Scope Global \ No newline at end of file diff --git a/ReportingServicesTools/Functions/CatalogItems/Rest/Test-RsRestItemDataSource.ps1 b/ReportingServicesTools/Functions/CatalogItems/Rest/Test-RsRestItemDataSource.ps1 new file mode 100644 index 00000000..72e0894d --- /dev/null +++ b/ReportingServicesTools/Functions/CatalogItems/Rest/Test-RsRestItemDataSource.ps1 @@ -0,0 +1,148 @@ +function Test-RsRestItemDataSource +{ + <# + .SYNOPSIS + This function fetches the DataSources from a Paginated or Power BI report and tests them to see if the connection can be made. + + .DESCRIPTION + This function fetches the DataSources from a Paginated or Power BI report and tests them to see if the connection can be made. + It tests using the connection information currently stopred in the report. + + .PARAMETER RsReport + Specify the location of the Power BI report for which the DataSources should be fetched. + + .PARAMETER ReportPortalUri + Specify the Report Portal URL to your Power BI Report Server Instance. + + .PARAMETER RestApiVersion + Specify the version of REST Endpoint to use. Valid values are: "v2.0". + + .PARAMETER Credential + Specify the credentials to use when connecting to the Report Server. + + .PARAMETER WebSession + Specify the session to be used when making calls to REST Endpoint. + + .EXAMPLE + Test-RsRestItemDataSource -RsReport "/MyReport" + Description + ----------- + Fetches all the DataSources for the "MyReport" catalog item found in "/" folder from the Report Server located at http://localhost/reports, and tests them to see if they can connect. + + .EXAMPLE + Test-RsRestItemDataSource -RsReport "/MyReport" -WebSession $session + Description + ----------- + Fetches all the DataSources for the "MyReport" catalog item found in "/" folder from the Report Server located at specificed WebSession object, and tests them to see if they can connect. + + .EXAMPLE + Test-RsRestItemDataSource -RsReport "/MyReport" -ReportPortalUri http://myserver/reports + Description + ----------- + Fetches all the DataSources for the "MyReport" catalog item found in "/" folder from the Report Server located at http://myserver/reports, and tests them to see if they can connect. + #> + + [CmdletBinding()] + param( + [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true)] + [Alias('ItemPath','Path', 'RsItem')] + [string] + $RsReport, + + [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true)] + [string] + $ReportPortalUri, + + [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true)] + [Alias('ApiVersion')] + [ValidateSet("v2.0")] + [string] + $RestApiVersion = "v2.0", + + [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true)] + [Alias('ReportServerCredentials')] + [System.Management.Automation.PSCredential] + $Credential, + + [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true)] + [Microsoft.PowerShell.Commands.WebRequestSession] + $WebSession + ) + Begin + { + $WebSession = New-RsRestSessionHelper -BoundParameters $PSBoundParameters + if ($null -ne $WebSession.Credentials -and $null -eq $Credential) { + Write-Verbose "Using credentials from WebSession" + $Credential = New-Object System.Management.Automation.PSCredential "$($WebSession.Credentials.UserName)@$($WebSession.Credentials.Domain)", $WebSession.Credentials.SecurePassword + } + $ReportPortalUri = Get-RsPortalUriHelper -WebSession $WebSession + } + Process + { + + if ($Credential -ne $null) + { + $Report = Get-RsRestItem -ReportPortalUri $ReportPortalUri -RsItem $RsReport -Credential $Credential -Verbose:$false + $ReportDataSources = Get-RsRestItemDataSource -ReportPortalUri $ReportPortalUri -RsItem $Report.Path -WebSession $WebSession -Credential $Credential -Verbose:$false + } + else + { + $Report = Get-RsRestItem -ReportPortalUri $ReportPortalUri -RsItem $RsReport -WebSession $WebSession -Verbose:$false + $ReportDataSources = Get-RsRestItemDataSource -ReportPortalUri $ReportPortalUri -RsItem $Report.Path -WebSession $WebSession -Verbose:$false + } + + $DataSourceResponses = @() + if($Report.Type -eq 'Report'){ + <# Paginated #> + foreach($ReportDataSource in $ReportDataSources){ + $payload = @{ + "DataSourceName" = "$($ReportDataSource.Name)" + } + $payloadJson = ConvertTo-Json $payload -Depth 15 + + $DataSourceConnectionUri = $ReportPortalUri + "api/$RestApiVersion/Reports({0})/Model.CheckDataSourceConnection" + $DataSourceConnectionUri = [String]::Format($DataSourceConnectionUri, $Report.Id) + Write-Verbose "$DataSourceConnectionUri" + if ($Credential -ne $null) + { + $PostResponse = Invoke-RestMethod -Uri $DataSourceConnectionUri -Method Post -Body ([System.Text.Encoding]::UTF8.GetBytes($payloadJson)) -ContentType "application/json" -Credential $Credential -Verbose:$false + } + else + { + $PostResponse = Invoke-RestMethod -Uri $DataSourceConnectionUri -Method Post -Body ([System.Text.Encoding]::UTF8.GetBytes($payloadJson)) -ContentType "application/json" -WebSession $WebSession -UseDefaultCredentials -Verbose:$false + } + $PostResponse | Add-Member -MemberType NoteProperty -Name ReportName -Value $Report.Name + $PostResponse | Add-Member -MemberType NoteProperty -Name DataSourceName -Value $ReportDataSource.Name + $PostResponse | Add-Member -MemberType NoteProperty -Name DataSourceId -Value $ReportDataSource.Id + $DataSourceResponses += $PostResponse + } + return $DataSourceResponses + } + elseif($Report.Type -eq 'PowerBIReport'){ + <# PBI #> + foreach($ReportDataSource in $ReportDataSources){ + $payload = @{ + "DataSourceName" = "$($ReportDataSource.id)" + } + $payloadJson = ConvertTo-Json $payload -Depth 15 + + $DataSourceConnectionUri = $ReportPortalUri + "api/$RestApiVersion/PowerBIReports({0})/Model.CheckDataSourceConnection" + $DataSourceConnectionUri = [String]::Format($DataSourceConnectionUri, $Report.Id) + Write-Verbose "$DataSourceConnectionUri" + if ($Credential -ne $null) + { + $PostResponse = Invoke-RestMethod -Uri $DataSourceConnectionUri -Method Post -Body ([System.Text.Encoding]::UTF8.GetBytes($payloadJson)) -ContentType "application/json" -Credential $Credential -Verbose:$false + } + else + { + $PostResponse = Invoke-RestMethod -Uri $DataSourceConnectionUri -Method Post -Body ([System.Text.Encoding]::UTF8.GetBytes($payloadJson)) -ContentType "application/json" -WebSession $WebSession -UseDefaultCredentials -Verbose:$false + } + $PostResponse | Add-Member -MemberType NoteProperty -Name ReportName -Value $Report.Name + $PostResponse | Add-Member -MemberType NoteProperty -Name DataSourceName -Value $ReportDataSource.Name + $PostResponse | Add-Member -MemberType NoteProperty -Name DataSourceId -Value $ReportDataSource.Id + $DataSourceResponses += $PostResponse + } + return $DataSourceResponses + } + } +} diff --git a/ReportingServicesTools/Functions/CatalogItems/Rest/Write-RsRestCatalogItem.ps1 b/ReportingServicesTools/Functions/CatalogItems/Rest/Write-RsRestCatalogItem.ps1 index 4622f394..50a66feb 100644 --- a/ReportingServicesTools/Functions/CatalogItems/Rest/Write-RsRestCatalogItem.ps1 +++ b/ReportingServicesTools/Functions/CatalogItems/Rest/Write-RsRestCatalogItem.ps1 @@ -17,16 +17,16 @@ function Write-RsRestCatalogItem Folder on reportserver to upload the item to. .PARAMETER Description - Specify the description to be added to the report. + Specify the description to be added to the report. PowerBIReport files larger than MinLargeFileSizeInMb are not affected by this parameter. .PARAMETER Overwrite - Overwrite the old entry, if an existing catalog item with same name exists at the specified destination. + Overwrite the old entry, if an existing catalog item with same name exists at the specified destination. PowerBIReport files larger than MinLargeFileSizeInMb are not affected by this parameter. .PARAMETER ReportPortalUri Specify the Report Portal URL to your SQL Server Reporting Services Instance. .PARAMETER RestApiVersion - Specify the version of REST Endpoint to use. Valid values are: "v1.0", "v2.0". + Specify the version of REST Endpoint to use. Valid values are: "v1.0", "v2.0". NOTE: - v1.0 of REST Endpoint is not supported by Microsoft and is for SSRS 2016. - v2.0 of REST Endpoint is supported by Microsoft and is for SSRS 2017, PBIRS October 2017 and newer releases. @@ -37,6 +37,15 @@ function Write-RsRestCatalogItem .PARAMETER WebSession Specify the session to be used when making calls to REST Endpoint. + .PARAMETER MaxFileSizeInMb + Specify the maximum file size for the PBIX report. + + .PARAMETER MinLargeFileSizeInMb + Specify the smallest possible size for a large PBIX report. + + .PARAMETER Hidden + Specify the item as hidden on the Report Server. PowerBIReport files larger than MinLargeFileSizeInMb are not affected by this parameter. + .EXAMPLE Write-RsRestCatalogItem -Path 'c:\reports\monthlyreport.rdl' -RsFolder '/monthlyreports' @@ -64,6 +73,13 @@ function Write-RsRestCatalogItem Description ----------- Uploads the report 'monthlyreport.rdl' to folder '/monthlyreports' using v2.0 REST Endpoint to Report Server located at http://myserver/reports. + + .EXAMPLE + Write-RsRestCatalogItem -Path 'c:\reports\monthlyreport.rdl' -RsFolder '/monthlyreports' -Hidden + + Description + ----------- + Uploads the report 'monthlyreport.rdl' as a hidden report to folder '/monthlyreports' using v2.0 REST Endpoint to Report Server located at http://localhost/reports/. #> [CmdletBinding()] param( @@ -96,11 +112,25 @@ function Write-RsRestCatalogItem $Credential, [Microsoft.PowerShell.Commands.WebRequestSession] - $WebSession + $WebSession, + + [float] + $MaxFileSizeInMb = 2000, + + [float] + $MinLargeFileSizeInMb = 25, + + [switch] + $Hidden ) Begin { $WebSession = New-RsRestSessionHelper -BoundParameters $PSBoundParameters + if ($null -ne $WebSession.Credentials -and $null -eq $Credential) + { + Write-Verbose "Using credentials from WebSession" + $Credential = New-Object System.Management.Automation.PSCredential "$($WebSession.Credentials.UserName)@$($WebSession.Credentials.Domain)", $WebSession.Credentials.SecurePassword + } $ReportPortalUri = Get-RsPortalUriHelper -WebSession $WebSession $catalogItemsUri = $ReportPortalUri + "api/$RestApiVersion/CatalogItems" if ($RestApiVersion -eq "v1.0") @@ -110,6 +140,7 @@ function Write-RsRestCatalogItem else { $catalogItemsByPathApi = $ReportPortalUri + "api/$RestApiVersion/CatalogItems(Path='{0}')" + $powerBIReportsByPathApi = $ReportPortalUri + "api/$RestApiVersion/PowerBIReports(Path='{0}')/Model.Upload" } $catalogItemsUpdateUri = $ReportPortalUri + "api/$RestApiVersion/CatalogItems({0})" } @@ -205,7 +236,7 @@ function Write-RsRestCatalogItem "CredentialRetrieval" = $credentialRetrieval; "CredentialsByUser" = $null; "CredentialsInServer" = $null; - "Hidden" = $false; + "Hidden" = $Hidden.IsPresent; "IsConnectionStringOverridden" = $true; "IsEnabled" = $enabled; } @@ -223,40 +254,95 @@ function Write-RsRestCatalogItem $content = [System.IO.File]::ReadAllText($EntirePath) $payload = ConvertFrom-Json $content $payload.Path = $itemPath + if ($Hidden.IsPresent -eq $true) + { + $payload.Hidden = $Hidden.IsPresent + } } else { - $bytes = [System.IO.File]::ReadAllBytes($EntirePath) - $payload = @{ - "@odata.type" = "#Model.$itemType"; - "Content" = [System.Convert]::ToBase64String($bytes); - "ContentType"=""; - "Name" = $itemName; - "Description" = $Description - "Path" = $itemPath; + $fileBytes = [System.IO.File]::ReadAllBytes($EntirePath) + $fileSizeInMb = (Get-Item $EntirePath).length/1MB + + if ($itemType -eq "PowerBIReport" -and $fileSizeInMb -ge $MinLargeFileSizeInMb) + { + $maxServerFileSizeInMb = Get-RsRestPublicServerSetting -Property "MaxFileSizeMb" -ReportPortalUri $ReportPortalUri -WebSession $WebSession + if ($fileSizeInMb -gt $MaxFileSizeInMb) + { + throw "This file is too large to be uploaded. Files larger than $MaxFileSizeInMb MB are not currently supported: $item!" + } + elseif ($maxServerFileSizeInMb -gt 0 -and $fileSizeInMb -gt $maxServerFileSizeInMb) + { + throw "This file is too large to be uploaded. Files larger than $maxServerFileSizeInMb MB are not currently supported: $item!" + } + + Write-Verbose "PowerBIReport $item is large. Properties Overwrite, Description, and Hidden are being ignored during upload" + + $isLargePowerBIReport = $true + $pbixPayload = [System.Text.Encoding]::GetEncoding('ISO-8859-1').GetString($fileBytes) + $boundary = [System.Guid]::NewGuid().ToString() + $LF = "`r`n" + + # endpoint handles a file and nothing else + # https://app.swaggerhub.com/apis-docs/microsoft-rs/pbirs/2.0#/PowerBIReports/UploadPowerBIReport + $bodyLines = ( + # File + "--$boundary", + "Content-Disposition: form-data; name=`"File`"; filename=`"$itemName`"", + "Content-Type: application/octet-stream$LF", + $pbixPayload, + "--$boundary--" + ) -join $LF + } + else + { + Write-Verbose "$item is small" + + $isLargePowerBIReport = $false + $payload = @{ + "@odata.type" = "#Model.$itemType"; + "Content" = [System.Convert]::ToBase64String($fileBytes); + "ContentType"=""; + "Name" = $itemName; + "Description" = $Description; + "Path" = $itemPath; + "Hidden" = $Hidden.IsPresent; + } } } - try + if ($itemType -eq "PowerBIReport" -and $isLargePowerBIReport -eq $true) + { + Write-Verbose "Uploading $EntirePath to $RsFolder via endpoint for large files..." + $endpointUrl = [String]::Format($powerBIReportsByPathApi, $itemPath) + $contentType = "multipart/form-data; boundary=$boundary" + $requestBody = $bodyLines + } + else { Write-Verbose "Uploading $EntirePath to $RsFolder..." - + $endpointUrl = $catalogItemsUri + $contentType = "application/json" $payloadJson = ConvertTo-Json $payload + $requestBody = ([System.Text.Encoding]::UTF8.GetBytes($payloadJson)) + } + try + { if ($Credential -ne $null) { - Invoke-WebRequest -Uri $catalogItemsUri -Method Post -WebSession $WebSession -Body ([System.Text.Encoding]::UTF8.GetBytes($payloadJson)) -ContentType "application/json" -Credential $Credential -Verbose:$false | Out-Null + Invoke-WebRequest -Uri $endpointUrl -Method Post -WebSession $WebSession -Body $requestBody -ContentType $contentType -Credential $Credential -UseBasicParsing -Verbose:$false | Out-Null } else { - Invoke-WebRequest -Uri $catalogItemsUri -Method Post -WebSession $WebSession -Body ([System.Text.Encoding]::UTF8.GetBytes($payloadJson)) -ContentType "application/json" -UseDefaultCredentials -Verbose:$false | Out-Null + Invoke-WebRequest -Uri $endpointUrl -Method Post -WebSession $WebSession -Body $requestBody -ContentType $contentType -UseDefaultCredentials -UseBasicParsing -Verbose:$false | Out-Null } Write-Verbose "$EntirePath was uploaded to $RsFolder successfully!" } catch { - if ($_.Exception.Response -ne $null -and $_.Exception.Response.StatusCode -eq 409 -and $Overwrite) + if ($isLargePowerBIReport -ne $true -and $_.Exception.Response -ne $null -and $_.Exception.Response.StatusCode -eq 409 -and $Overwrite) { try { @@ -264,11 +350,11 @@ function Write-RsRestCatalogItem $uri = [String]::Format($catalogItemsByPathApi, $itemPath) if ($Credential -ne $null) { - $response = Invoke-WebRequest -Uri $uri -Method Get -WebSession $WebSession -Credential $Credential -Verbose:$false + $response = Invoke-WebRequest -Uri $uri -Method Get -WebSession $WebSession -Credential $Credential -UseBasicParsing -Verbose:$false } else { - $response = Invoke-WebRequest -Uri $uri -Method Get -WebSession $WebSession -UseDefaultCredentials -Verbose:$false + $response = Invoke-WebRequest -Uri $uri -Method Get -WebSession $WebSession -UseDefaultCredentials -UseBasicParsing -Verbose:$false } # parsing response to get Id @@ -279,11 +365,11 @@ function Write-RsRestCatalogItem $uri = [String]::Format($catalogItemsUpdateUri, $itemId) if ($Credential -ne $null) { - Invoke-WebRequest -Uri $uri -Method Put -WebSession $WebSession -Body ([System.Text.Encoding]::UTF8.GetBytes($payloadJson)) -ContentType "application/json" -Credential $Credential -Verbose:$false | Out-Null + Invoke-WebRequest -Uri $uri -Method Put -WebSession $WebSession -Body $requestBody -ContentType "application/json" -Credential $Credential -UseBasicParsing -Verbose:$false | Out-Null } else { - Invoke-WebRequest -Uri $uri -Method Put -WebSession $WebSession -Body ([System.Text.Encoding]::UTF8.GetBytes($payloadJson)) -ContentType "application/json" -UseDefaultCredentials -Verbose:$false | Out-Null + Invoke-WebRequest -Uri $uri -Method Put -WebSession $WebSession -Body $requestBody -ContentType "application/json" -UseDefaultCredentials -UseBasicParsing -Verbose:$false | Out-Null } Write-Verbose "$EntirePath was uploaded to $RsFolder successfully!" } diff --git a/ReportingServicesTools/Functions/CatalogItems/Rest/Write-RsRestFolderContent.ps1 b/ReportingServicesTools/Functions/CatalogItems/Rest/Write-RsRestFolderContent.ps1 index 09113730..1ff8ac45 100644 --- a/ReportingServicesTools/Functions/CatalogItems/Rest/Write-RsRestFolderContent.ps1 +++ b/ReportingServicesTools/Functions/CatalogItems/Rest/Write-RsRestFolderContent.ps1 @@ -97,6 +97,10 @@ function Write-RsRestFolderContent Begin { $WebSession = New-RsRestSessionHelper -BoundParameters $PSBoundParameters + if ($null -ne $WebSession.Credentials -and $null -eq $Credential) { + Write-Verbose "Using credentials from WebSession" + $Credential = New-Object System.Management.Automation.PSCredential "$($WebSession.Credentials.UserName)@$($WebSession.Credentials.Domain)", $WebSession.Credentials.SecurePassword + } $ReportPortalUri = Get-RsPortalUriHelper -WebSession $WebSession $catalogItemsUri = $ReportPortalUri + "api/$RestApiVersion/CatalogItems" $folderUri = $ReportPortalUri + "api/$RestApiVersion/Folders(Path='{0}')" @@ -154,11 +158,11 @@ function Write-RsRestFolderContent # Try to get folder info if ($Credential -ne $null) { - $response = Invoke-WebRequest -Uri $uri -Method Get -WebSession $WebSession -Credential $Credential -Verbose:$false + $response = Invoke-WebRequest -Uri $uri -Method Get -WebSession $WebSession -Credential $Credential -UseBasicParsing -Verbose:$false } else { - $response = Invoke-WebRequest -Uri $uri -Method Get -WebSession $WebSession -UseDefaultCredentials -Verbose:$false + $response = Invoke-WebRequest -Uri $uri -Method Get -WebSession $WebSession -UseDefaultCredentials -UseBasicParsing -Verbose:$false } # parsing response to get folder name diff --git a/ReportingServicesTools/Functions/CatalogItems/Set-RsSubscription.ps1 b/ReportingServicesTools/Functions/CatalogItems/Set-RsSubscription.ps1 index 54342ba9..aecc635d 100644 --- a/ReportingServicesTools/Functions/CatalogItems/Set-RsSubscription.ps1 +++ b/ReportingServicesTools/Functions/CatalogItems/Set-RsSubscription.ps1 @@ -32,8 +32,10 @@ function Set-RsSubscription Used to change the EndDate of subscription. .PARAMETER Owner - Used to change the owner of a subscription. + Used to change the owner of a subscription. + .PARAMETER SubProperties + Subscription object returned from Get-RsSubscription .EXAMPLE Get-RsSubscription -path '/Finance/ImportantReports' | Set-RsSubscription -EndDate 9/9/2099 @@ -126,10 +128,10 @@ try } - if ($StartDateTime -ne $null -or $EndDate -ne $null) + if ($StartDateTime -ne $null -or $EndDate -ne $null -or $SubProperties -ne $null) { $null = $Proxy.SetSubscriptionProperties($SubProperties.subscriptionID, $SubProperties.DeliverySettings, $SubProperties.Description, $SubProperties.EventType, $XMLMatch.OuterXml, $SubProperties.Values) - Write-Verbose "subscription $($SubProperties.subscriptionId) for $($SubProperties.report) report successfully updated!" + Write-Verbose "Subscription $($SubProperties.subscriptionId) for $($SubProperties.report) report successfully updated!" } } Catch diff --git a/ReportingServicesTools/Functions/CatalogItems/Write-RsCatalogItem.ps1 b/ReportingServicesTools/Functions/CatalogItems/Write-RsCatalogItem.ps1 index 667c5c0f..5e83cd37 100644 --- a/ReportingServicesTools/Functions/CatalogItems/Write-RsCatalogItem.ps1 +++ b/ReportingServicesTools/Functions/CatalogItems/Write-RsCatalogItem.ps1 @@ -159,7 +159,7 @@ function Write-RsCatalogItem { throw "Data Source Definition not found in the specified file: $EntirePath!" } - + $NewRsDataSourceParam = @{ Proxy = $Proxy RsFolder = $RsFolder @@ -169,11 +169,12 @@ function Write-RsCatalogItem Disabled = ("false" -like $content.DataSourceDefinition.Enabled) CredentialRetrieval = 'None' Overwrite = $Overwrite + Hidden = $Hidden } } elseif ($item.Extension -eq '.rds') { - if ($content -eq $null -or + if ($content -eq $null -or $content.RptDataSource -eq $null -or $content.RptDataSource.Name -eq $null -or $content.RptDataSource.ConnectionProperties -eq $null -or @@ -203,6 +204,7 @@ function Write-RsCatalogItem Disabled = $false CredentialRetrieval = $credentialRetrieval Overwrite = $Overwrite + Hidden = $Hidden } if ($credentialRetrieval -eq "prompt") @@ -257,7 +259,7 @@ function Write-RsCatalogItem $hiddenProperty.Value = $Hidden $additionalProperties.Add($hiddenProperty) } - + $bytes = [System.IO.File]::ReadAllBytes($EntirePath) $warnings = $null try diff --git a/ReportingServicesTools/Functions/Common/Get-ItemType.ps1 b/ReportingServicesTools/Functions/Common/Get-ItemType.ps1 index 07b1b6df..265ab074 100644 --- a/ReportingServicesTools/Functions/Common/Get-ItemType.ps1 +++ b/ReportingServicesTools/Functions/Common/Get-ItemType.ps1 @@ -6,15 +6,16 @@ ) switch ($FileExtension) { - '.rdl' { return 'Report' } - '.rsds' { return 'DataSource' } - '.rds' { return 'DataSource' } - '.rsd' { return 'DataSet' } + '.rdl' { return 'Report' } + '.rsds' { return 'DataSource' } + '.rds' { return 'DataSource' } + '.rsd' { return 'DataSet' } '.rsmobile' { return 'MobileReport' } - '.pbix' { return 'PowerBIReport' } - '.xls' { return 'ExcelWorkbook' } - '.xlsx' { return 'ExcelWorkbook' } - '.kpi' { return 'Kpi' } - default { return 'Resource' } + '.pbix' { return 'PowerBIReport' } + '.xls' { return 'ExcelWorkbook' } + '.xlsx' { return 'ExcelWorkbook' } + '.xlsm' { return 'ExcelWorkbook' } + '.kpi' { return 'Kpi' } + default { return 'Resource' } } } \ No newline at end of file diff --git a/ReportingServicesTools/Functions/Common/MakeDeploymentFolders.ps1 b/ReportingServicesTools/Functions/Common/MakeDeploymentFolders.ps1 new file mode 100644 index 00000000..c5880840 --- /dev/null +++ b/ReportingServicesTools/Functions/Common/MakeDeploymentFolders.ps1 @@ -0,0 +1,18 @@ +function MakeDeploymentFolders { + param($RsFolder,$ReportPortal) + $tree=$null + $tree + $Base='/' + ($RsFolder.substring(1,$RsFolder.length-1)).split('/') | foreach{ + $Folder = $_ + $tree += "/"+$Folder + try{ + Get-RsRestItem -ReportPortalUri $ReportPortal -RsItem $tree| ft -AutoSize + } + catch{ + Write-Warning "Folder $tree does not exist"; + New-RsRestFolder -ReportPortalUri $ReportPortal -RsFolder $Base -FolderName $Folder -Verbose + } + $Base=$tree + } +} \ No newline at end of file diff --git a/ReportingServicesTools/Functions/Common/New-RestSessionHelperSplat.ps1 b/ReportingServicesTools/Functions/Common/New-RestSessionHelperSplat.ps1 new file mode 100644 index 00000000..b728356e --- /dev/null +++ b/ReportingServicesTools/Functions/Common/New-RestSessionHelperSplat.ps1 @@ -0,0 +1,72 @@ +function New-RestSessionHelperSplat +{ + <# + .SYNOPSIS + Internal helper function. Facilitates generating Rest Session objects. + + .DESCRIPTION + Internal helper function. Facilitates generating Rest Session objects. + It is an enhancement to the New-RsRestSession function - containing Credentials and UseBasicParsing. + This allows simpler usage and avoids duplicated calls (Credential/UseDefaultCredentials) + + It accepts all bound parameters of the calling function and processes the following keys: + - ReportPortalUri + - RestApiVersion + - Credential + - WebSession + These parameters are passed on to the New-RsRestSession function, unless WebSession was specified. + If the bound parameters contain the WebSession parameter, the function will return that object. + All other bound parameters are ignored. + + .PARAMETER BoundParameters + The bound parameters of the calling function + + .EXAMPLE + $RsRestWebSession = New-RestSessionHelperSplat -BoundParameters $PSBoundParameters + + Generates or retrieves a WebSession object and returns a hashtable to be used in further webrequest calls. + + .RETURNS + A hastable containing all properties to be passed to a Invoke-Webrequest call using splatting. + #> + + [CmdletBinding()] + Param ( + [AllowNull()] + [object] + $BoundParameters + ) + + if ($BoundParameters["WebSession"]) + { + $session = $WebSession + } else { + $goodKeys = @("ReportPortalUri", "RestApiVersion", "Credential") + $NewRsRestSessionParams = @{ } + + foreach ($key in $BoundParameters.Keys) + { + if ($goodKeys -contains $key) + { + $NewRsRestSessionParams[$key] = $BoundParameters[$key] + } + } + + $session = New-RsRestSession @NewRsRestSessionParams + } + if ($null -ne $session.Credentials -and $null -eq $Credential) { + Write-Verbose "Using credentials from WebSession" + $Credential = New-Object System.Management.Automation.PSCredential "$($session.Credentials.UserName)@$($session.Credentials.Domain)", $session.Credentials.SecurePassword + } + $splatInvokeWebRequest = @{ + WebSession = $session + UseBasicParsing = $true + Verbose = $false + } + if ($null -ne $Credential) { + $splatInvokeWebRequest.Add("Credential", $Credential) + } else { + $splatInvokeWebRequest.Add("UseDefaultCredentials", $true) + } + $splatInvokeWebRequest +} \ No newline at end of file diff --git a/ReportingServicesTools/Functions/Security/Grant-RsCatalogItemRole.ps1 b/ReportingServicesTools/Functions/Security/Grant-RsCatalogItemRole.ps1 index 8c02931e..efafdea2 100644 --- a/ReportingServicesTools/Functions/Security/Grant-RsCatalogItemRole.ps1 +++ b/ReportingServicesTools/Functions/Security/Grant-RsCatalogItemRole.ps1 @@ -54,6 +54,12 @@ function Grant-RsCatalogItemRole Description ----------- This command will establish a connection to the Report Server located at http://localhost/reportserver using CaptainAwesome's credentials and then grant Browser access to user 'johnd' on catalog item found at '/My Folder/SalesReport'. + + .EXAMPLE + Grant-RsCatalogItemRole -Identity 'CONTOSO\Report_Developers' -RoleName 'Browser' -Path '/Finance' -ReportServerUri https://UATPBIRS/reportserver + Description + ----------- + This command will grant Browser access to members of the 'Report_Developers' domain group to catalog items found under the '/Finance' folder. It will do this by establishing a connection to the Report Server located at https://UATPBIRS/reportserver using current user's credentials. #> [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')] diff --git a/ReportingServicesTools/Functions/Security/Rest/Get-RsRestItemAccessPolicy.ps1 b/ReportingServicesTools/Functions/Security/Rest/Get-RsRestItemAccessPolicy.ps1 new file mode 100644 index 00000000..4eacf7e7 --- /dev/null +++ b/ReportingServicesTools/Functions/Security/Rest/Get-RsRestItemAccessPolicy.ps1 @@ -0,0 +1,199 @@ +# Copyright (c) 2021 Microsoft Corporation. All Rights Reserved. +# Licensed under the MIT License (MIT) + +function Get-RsRestItemAccessPolicy +{ + <# + .SYNOPSIS + This function retrieves access policies to SQL Server Reporting Services Instance or Power BI Report Server Instance from users/groups. + + .DESCRIPTION + This function retrieves all access policies on the SQL Server Reporting Services Instance or Power BI Report Server Instance located at the specified Report Server URI from the specified user/group. + + .PARAMETER RsItem + Specify the path to catalog item on the server. + + .PARAMETER Recurse + Recursively list subfolders with content. + + .PARAMETER ReportPortalUri + Specify the Report Portal URL to your SQL Server Reporting Services Instance. + + .PARAMETER RestApiVersion + Specify the version of REST Endpoint to use. Valid values are: "v2.0". + + .PARAMETER Credential + Specify the credentials to use when connecting to the Report Server. + + .PARAMETER WebSession + Specify the session to be used when making calls to REST Endpoint. + + .EXAMPLE + Get-RsRestItemAccessPolicy -RsItem "/MyReport" + Description + ----------- + Fetches Policy object for the "MyReport" catalog item found in "/" folder from the Report Server located at http://localhost/reports and returns all access for all users & groups. + .EXAMPLE + Get-RsRestItemAccessPolicy -RsItem "/MyReport" -Identity 'jeremymcgee' + Description + ----------- + Fetches Policy object the "MyReport" catalog item found in "/" folder from the Report Server located at http://localhost/reports using current user's credentials and then retrieves all access for user 'jmcgee'. + .EXAMPLE + Get-RsRestItemAccessPolicy -RsItem "/MyReport" -ReportPortalUri http://myserver/reports + Description + ----------- + Fetches Policy object for the "MyReport" catalog item found in "/" folder from the Report Server located at http://myserver/reports and returns all access for all users & groups. + .EXAMPLE + Get-RsRestItemAccessPolicy -RsItem "/Finance" -ReportPortalUri http://myserver/reports -Identity 'jeremymcgee' -Recurse + Description + ----------- + This command will establish a connection to the Report Server located at http://localhost/reports using current user's credentials and then retrieves all access for user 'jmcgee' recursively. + #> + + [CmdletBinding()] + param( + [Parameter(Mandatory = $True, ValueFromPipelineByPropertyName = $true)] + [Alias('ItemPath','Path', 'RsReport')] + [string] + $RsItem, + + [string] + $Identity, + + [switch] + $Recurse, + + [string] + $ReportPortalUri, + + [Alias('ApiVersion')] + [ValidateSet("v2.0")] + [string] + $RestApiVersion = "v2.0", + + [Alias('ReportServerCredentials')] + [System.Management.Automation.PSCredential] + $Credential, + + [Microsoft.PowerShell.Commands.WebRequestSession] + $WebSession + ) + Begin + { + $WebSession = New-RsRestSessionHelper -BoundParameters $PSBoundParameters + if ($null -ne $WebSession.Credentials -and $null -eq $Credential) { + Write-Verbose "Using credentials from WebSession" + $Credential = New-Object System.Management.Automation.PSCredential "$($WebSession.Credentials.UserName)@$($WebSession.Credentials.Domain)", $WebSession.Credentials.SecurePassword + } + $ReportPortalUri = Get-RsPortalUriHelper -WebSession $WebSession + $PolicyUri = $ReportPortalUri + "api/$RestApiVersion/CatalogItems({0})/Policies" + } + Process + { + try + { + Write-Verbose "Fetching metadata for $RsItem..." + if ($Credential -ne $null) + { + $Item = Get-RsRestItem -ReportPortalUri $ReportPortalUri -RsItem $RsItem -WebSession $WebSession -Credential $Credential -Verbose:$false + } + else + { + $Item = Get-RsRestItem -ReportPortalUri $ReportPortalUri -RsItem $RsItem -WebSession $WebSession -Verbose:$false + } + + Write-Verbose "Fetching Policies for $RsItem..." + $PolicyUri = [String]::Format($PolicyUri, $Item.Id) + if ($Credential -ne $null) + { + $response = Invoke-RestMethod -Uri $PolicyUri -Method Get -WebSession $WebSession -Credential $Credential -Verbose:$false + } + else + { + $response = Invoke-RestMethod -Uri $PolicyUri -Method Get -WebSession $WebSession -UseDefaultCredentials -Verbose:$false + } + + $catalogItemRoles = @() + $InheritParentPolicy = $response.InheritParentPolicy + + # Filter Polices by Identity + if($Identity) { + $response.Policies = $response.Policies | Where-Object { $_.GroupUserName -eq $Identity } + } + + if($response.Policies) { + $response.Policies | ForEach-Object { + + $catalogItemRole = New-Object -TypeName PSCustomObject + $catalogItemRole | Add-Member -MemberType NoteProperty -Name Identity -Value $_.GroupUserName + $catalogItemRole | Add-Member -MemberType NoteProperty -Name Path -Value $Item.Path + $catalogItemRole | Add-Member -MemberType NoteProperty -Name TypeName -Value $Item.Type + $catalogItemRole | Add-Member -MemberType NoteProperty -Name Roles -Value $_.Roles.Name + $catalogItemRole | Add-Member -MemberType NoteProperty -Name ParentSecurity -Value $InheritParentPolicy + + $catalogItemRoles += $catalogItemRole + } + } + + if($Recurse -and $Item.Type -eq "Folder") { + + $GetRsFolderContentParam = @{ + WebSession = $WebSession + RsFolder = $Item.Path + Recurse = $Recurse + ErrorAction = 'Stop' + } + + try + { + $ChildItems = Get-RsRestFolderContent @GetRsFolderContentParam + } + catch + { + throw (New-Object System.Exception("Failed to retrieve items in '$RsFolder': $($_.Exception.Message)", $_.Exception)) + } + + foreach($ChildItem in $ChildItems) + { + $PolicyUri = $ReportPortalUri + "api/$RestApiVersion/CatalogItems({0})/Policies" + $PolicyUri = [String]::Format($PolicyUri, $ChildItem.Id) + + if ($Credential -ne $null) + { + $childPolicies = Invoke-RestMethod -Uri $PolicyUri -Method Get -WebSession $WebSession -Credential $Credential -Verbose:$false + } + else + { + $childPolicies = Invoke-RestMethod -Uri $PolicyUri -Method Get -WebSession $WebSession -UseDefaultCredentials -Verbose:$false + } + + # Filter Polices by Identity + if($Identity) { + $childPolicies.Policies = $childPolicies.Policies | Where-Object { $_.GroupUserName -eq $Identity } + } + + if($childPolicies.Policies) { + $childPolicies.Policies | ForEach-Object { + + $catalogItemRole = New-Object -TypeName PSCustomObject + $catalogItemRole | Add-Member -MemberType NoteProperty -Name Identity -Value $_.GroupUserName + $catalogItemRole | Add-Member -MemberType NoteProperty -Name Path -Value $ChildItem.Path + $catalogItemRole | Add-Member -MemberType NoteProperty -Name TypeName -Value $ChildItem.Type + $catalogItemRole | Add-Member -MemberType NoteProperty -Name Roles -Value $_.Roles.Name + $catalogItemRole | Add-Member -MemberType NoteProperty -Name ParentSecurity -Value $InheritParentPolicy + + $catalogItemRoles += $catalogItemRole + } + } + + + } + } + return $catalogItemRoles + } + catch + { + throw (New-Object System.Exception("Failed to get access policies for '$RsItem': $($_.Exception.Message)", $_.Exception)) + } + } +} \ No newline at end of file diff --git a/ReportingServicesTools/Functions/Security/Rest/Grant-RsRestItemAccessPolicy.ps1 b/ReportingServicesTools/Functions/Security/Rest/Grant-RsRestItemAccessPolicy.ps1 new file mode 100644 index 00000000..0a94591a --- /dev/null +++ b/ReportingServicesTools/Functions/Security/Rest/Grant-RsRestItemAccessPolicy.ps1 @@ -0,0 +1,169 @@ +# Copyright (c) 2021 Microsoft Corporation. All Rights Reserved. +# Licensed under the MIT License (MIT) + +function Grant-RsRestItemAccessPolicy +{ + <# + .SYNOPSIS + This function grants access policies to SQL Server Reporting Services Instance or Power BI Report Server Instance from users/groups. + + .DESCRIPTION + This function grants all access policies on the SQL Server Reporting Services Instance or Power BI Report Server Instance located at the specified Report Server URI to the specified user/group. + + .PARAMETER RsItem + Specify the path to catalog item (folder or report) on the server. + + .PARAMETER Identity + Specify the user or group name to grant access to. + + .PARAMETER Role + Specify the name of the role you want to grant on the catalog item. + + .PARAMETER ReportPortalUri + Specify the Report Portal URL to your SQL Server Reporting Services or Power BI Report Server Instance. + + .PARAMETER RestApiVersion + Specify the version of REST Endpoint to use. Valid values are: "v2.0". + + .PARAMETER Credential + Specify the credentials to use when connecting to the Report Server. + + .PARAMETER WebSession + Specify the session to be used when making calls to REST Endpoint. + + .EXAMPLE + Grant-RsRestItemAccessPolicy -Identity 'johnd' -Role 'Browser' -Path '/My Folder/SalesReport' + Description + ----------- + This command will establish a connection to the Report Server located at http://localhost/reports using current user's credentials and then grant Browser access to user 'johnd' on catalog item found at '/My Folder/SalesReport'. + + .EXAMPLE + Grant-RsRestItemAccessPolicy -ReportPortalUri 'http://localhost/reports_sql2012' -Identity 'johnd' -Role 'Browser' -Path '/My Folder/SalesReport' + Description + ----------- + This command will establish a connection to the Report Server located at http://localhost/reports_sql2012 using current user's credentials and then grant Browser access to user 'johnd' on catalog item found at '/My Folder/SalesReport'. + + .EXAMPLE + Grant-RsRestItemAccessPolicy -Credential 'CaptainAwesome' -Identity 'johnd' -Role 'Browser' -Path '/My Folder/SalesReport' + Description + ----------- + This command will establish a connection to the Report Server located at http://localhost/reports using CaptainAwesome's credentials and then grant Browser access to user 'johnd' on catalog item found at '/My Folder/SalesReport'. + + .EXAMPLE + Grant-RsRestItemAccessPolicy -Identity 'CONTOSO\Report_Developers' -Role 'Browser' -RsItem '/Finance' -ReportPortalUri https://UATPBIRS/reports + Description + ----------- + This command will grant Browser access to members of the 'Report_Developers' domain group to catalog items found under the '/Finance' folder. It will do this by establishing a connection to the Report Server located at https://UATPBIRS/reports using current user's credentials. + #> + + [CmdletBinding()] + param( + [Parameter(Mandatory = $True, ValueFromPipelineByPropertyName = $true)] + [Alias('ItemPath','Path', 'RsReport')] + [string] + $RsItem, + + [Parameter(Mandatory = $True, ValueFromPipelineByPropertyName = $true)] + [string] + $Identity, + + [Parameter(Mandatory = $True, ValueFromPipelineByPropertyName = $true)] + [Alias('RoleName')] + [ValidateSet("Browser","Content Manager","My Reports","Publisher","Report Builder")] + [string] + $Role, + + [string] + $ReportPortalUri, + + [Alias('ApiVersion')] + [ValidateSet("v2.0")] + [string] + $RestApiVersion = "v2.0", + + [Alias('ReportServerCredentials')] + [System.Management.Automation.PSCredential] + $Credential, + + [Microsoft.PowerShell.Commands.WebRequestSession] + $WebSession + ) + Begin + { + $WebSession = New-RsRestSessionHelper -BoundParameters $PSBoundParameters + if ($null -ne $WebSession.Credentials -and $null -eq $Credential) { + Write-Verbose "Using credentials from WebSession" + $Credential = New-Object System.Management.Automation.PSCredential "$($WebSession.Credentials.UserName)@$($WebSession.Credentials.Domain)", $WebSession.Credentials.SecurePassword + } + $ReportPortalUri = Get-RsPortalUriHelper -WebSession $WebSession + } + Process + { + try + { + Write-Verbose "Fetching metadata for $RsItem..." + if ($Credential -ne $null) + { + $Item = Get-RsRestItem -ReportPortalUri $ReportPortalUri -RsItem $RsItem -WebSession $WebSession -Credential $Credential -Verbose:$false + } + else + { + $Item = Get-RsRestItem -ReportPortalUri $ReportPortalUri -RsItem $RsItem -WebSession $WebSession -Verbose:$false + } + + if($Item.Type -in 'Report', 'PowerBIReport'){ + Write-Verbose "Fetching Policies for $Item..." + $PolicyUri = $ReportPortalUri + "api/$RestApiVersion/CatalogItems({0})/Policies" + $PolicyUri = [String]::Format($PolicyUri, $Item.Id) + if ($Credential -ne $null) + { + $response = Invoke-RestMethod -Uri $PolicyUri -Method Get -WebSession $WebSession -Credential $Credential -Verbose:$false + } + else + { + $response = Invoke-RestMethod -Uri $PolicyUri -Method Get -WebSession $WebSession -UseDefaultCredentials -Verbose:$false + } + } + elseif ($Item.Type -eq 'Folder') { + Write-Verbose "Fetching Policies for $Item..." + $PolicyUri = $ReportPortalUri + "api/$RestApiVersion/Folders({0})/Policies" + $PolicyUri = [String]::Format($PolicyUri, $Item.Id) + if ($Credential -ne $null) + { + $response = Invoke-RestMethod -Uri $PolicyUri -Method Get -WebSession $WebSession -Credential $Credential -Verbose:$false + } + else + { + $response = Invoke-RestMethod -Uri $PolicyUri -Method Get -WebSession $WebSession -UseDefaultCredentials -Verbose:$false + } + } + + $o=[PSCustomObject]@{ + GroupUserName=$Identity + Roles=@([PSCustomObject]@{ + Name=$Role + Description='' + }) + } + + $response.Policies=$response.Policies+$o + $response.InheritParentPolicy=$false + + $payloadJson = $response | ConvertTo-Json -Depth 15 + Write-Verbose "$payloadJson" + if ($Credential -ne $null) + { + $response = Invoke-RestMethod -Uri $PolicyUri -Method Put -WebSession $WebSession -Credential $Credential -Body ([System.Text.Encoding]::UTF8.GetBytes($payloadJson)) -ContentType "application/json" -Verbose:$false + } + else + { + $response = Invoke-RestMethod -Uri $PolicyUri -Method Put -WebSession $WebSession -UseDefaultCredentials -Body ([System.Text.Encoding]::UTF8.GetBytes($payloadJson)) -ContentType "application/json" -Verbose:$false + } + return $response + } + catch + { + throw (New-Object System.Exception("Failed to grant access policies for '$RsItem': $($_.Exception.Message)", $_.Exception)) + } + } +} \ No newline at end of file diff --git a/ReportingServicesTools/Functions/Security/Rest/Revoke-RsRestItemAccessPolicy.ps1 b/ReportingServicesTools/Functions/Security/Rest/Revoke-RsRestItemAccessPolicy.ps1 new file mode 100644 index 00000000..9162e95e --- /dev/null +++ b/ReportingServicesTools/Functions/Security/Rest/Revoke-RsRestItemAccessPolicy.ps1 @@ -0,0 +1,147 @@ +# Copyright (c) 2021 Microsoft Corporation. All Rights Reserved. +# Licensed under the MIT License (MIT) + +function Revoke-RsRestItemAccessPolicy +{ + <# + .SYNOPSIS + This function revokes access to catalog items from users/groups. + + .DESCRIPTION + This function revokes all access on the specified catalog item from the specified user/group. + + .PARAMETER RsItem + Specify the path to catalog item (folder or report) on the server. + + .PARAMETER Identity + Specify the user or group name to revoke access from. + + .PARAMETER ReportPortalUri + Specify the Report Portal URL to your SQL Server Reporting Services or Power BI Report Server Instance. + + .PARAMETER RestApiVersion + Specify the version of REST Endpoint to use. Valid values are: "v2.0". + + .PARAMETER Credential + Specify the credentials to use when connecting to the Report Server. + + .PARAMETER WebSession + Specify the session to be used when making calls to REST Endpoint. + + .EXAMPLE + Revoke-RsRestItemAccessPolicy -Identity 'johnd' -RsItem '/My Folder/SalesReport' + Description + ----------- + This command will establish a connection to the Report Server located at http://localhost/reports using current user's credentials and then revoke all access granted to user 'johnd' on catalog item found at '/My Folder/SalesReport'. + + .EXAMPLE + Revoke-RsRestItemAccessPolicy -ReportPortalUri 'http://localhost/reports_sql2012' -Identity 'johnd' -RsItem '/My Folder/SalesReport' + Description + ----------- + This command will establish a connection to the Report Server located at http://localhost/reports_sql2012 using current user's credentials and then revoke all access granted to user 'johnd' on catalog item found at '/My Folder/SalesReport'. + + .EXAMPLE + Revoke-RsRestItemAccessPolicy -Identity 'CONTOSO\Report_Developers' -RsItem '/Finance' -ReportPortalUri https://UATPBIRS/reports + Description + ----------- + This command will revoke all access granted to members of the 'Report_Developers' domain group to catalog items found under the '/Finance' folder. It will do this by establishing a connection to the Report Server located at https://UATPBIRS/reports using current user's credentials. + #> + + [CmdletBinding()] + param( + [Parameter(Mandatory = $True, ValueFromPipelineByPropertyName = $true)] + [Alias('ItemPath','Path', 'RsReport')] + [string] + $RsItem, + + [Parameter(Mandatory = $True, ValueFromPipelineByPropertyName = $true)] + [string] + $Identity, + + [string] + $ReportPortalUri, + + [Alias('ApiVersion')] + [ValidateSet("v2.0")] + [string] + $RestApiVersion = "v2.0", + + [Alias('ReportServerCredentials')] + [System.Management.Automation.PSCredential] + $Credential, + + [Microsoft.PowerShell.Commands.WebRequestSession] + $WebSession + ) + Begin + { + $WebSession = New-RsRestSessionHelper -BoundParameters $PSBoundParameters + if ($null -ne $WebSession.Credentials -and $null -eq $Credential) { + Write-Verbose "Using credentials from WebSession" + $Credential = New-Object System.Management.Automation.PSCredential "$($WebSession.Credentials.UserName)@$($WebSession.Credentials.Domain)", $WebSession.Credentials.SecurePassword + } + $ReportPortalUri = Get-RsPortalUriHelper -WebSession $WebSession + } + Process + { + try + { + Write-Verbose "Fetching metadata for $RsItem..." + if ($Credential -ne $null) + { + $Item = Get-RsRestItem -ReportPortalUri $ReportPortalUri -RsItem $RsItem -WebSession $WebSession -Credential $Credential -Verbose:$false + } + else + { + $Item = Get-RsRestItem -ReportPortalUri $ReportPortalUri -RsItem $RsItem -WebSession $WebSession -Verbose:$false + } + + if($Item.Type -in 'Report', 'PowerBIReport'){ + Write-Verbose "Fetching Policies for $Item..." + $PolicyUri = $ReportPortalUri + "api/$RestApiVersion/CatalogItems({0})/Policies" + $PolicyUri = [String]::Format($PolicyUri, $Item.Id) + if ($Credential -ne $null) + { + $response = Invoke-RestMethod -Uri $PolicyUri -Method Get -WebSession $WebSession -Credential $Credential -Verbose:$false + } + else + { + $response = Invoke-RestMethod -Uri $PolicyUri -Method Get -WebSession $WebSession -UseDefaultCredentials -Verbose:$false + } + } + elseif ($Item.Type -eq 'Folder') { + Write-Verbose "Fetching Policies for $Item..." + $PolicyUri = $ReportPortalUri + "api/$RestApiVersion/Folders({0})/Policies" + $PolicyUri = [String]::Format($PolicyUri, $Item.Id) + if ($Credential -ne $null) + { + $response = Invoke-RestMethod -Uri $PolicyUri -Method Get -WebSession $WebSession -Credential $Credential -Verbose:$false + } + else + { + $response = Invoke-RestMethod -Uri $PolicyUri -Method Get -WebSession $WebSession -UseDefaultCredentials -Verbose:$false + } + } + + $response.Policies = @([PSCustomObject]$response.Policies | WHERE {$_.groupusername -ne $Identity}) + $response.InheritParentPolicy=$false + + $payloadJson = $response | ConvertTo-Json -Depth 15 + Write-Verbose "$payloadJson" + if ($Credential -ne $null) + { + $response = Invoke-RestMethod -Uri $PolicyUri -Method Put -WebSession $WebSession -Credential $Credential -Body ([System.Text.Encoding]::UTF8.GetBytes($payloadJson)) -ContentType "application/json" -Verbose:$false + } + else + { + $response = Invoke-RestMethod -Uri $PolicyUri -Method Put -WebSession $WebSession -UseDefaultCredentials -Body ([System.Text.Encoding]::UTF8.GetBytes($payloadJson)) -ContentType "application/json" -Verbose:$false + } + + return $response + } + catch + { + throw (New-Object System.Exception("Failed to revoke access policies for '$RsItem': $($_.Exception.Message)", $_.Exception)) + } + } +} diff --git a/ReportingServicesTools/Functions/Utilities/New-RsRestSession.ps1 b/ReportingServicesTools/Functions/Utilities/New-RsRestSession.ps1 index fdbb9af1..a643943d 100644 --- a/ReportingServicesTools/Functions/Utilities/New-RsRestSession.ps1 +++ b/ReportingServicesTools/Functions/Utilities/New-RsRestSession.ps1 @@ -71,11 +71,11 @@ function New-RsRestSession Write-Verbose "Making call to $meUri to create a session..." if ($Credential) { - $result = Invoke-WebRequest -Uri $meUri -Credential $Credential -SessionVariable mySession -Verbose:$false -ErrorAction Stop + $result = Invoke-WebRequest -Uri $meUri -Credential $Credential -SessionVariable mySession -UseBasicParsing -Verbose:$false -ErrorAction Stop } else { - $result = Invoke-WebRequest -Uri $meUri -UseDefaultCredentials -SessionVariable mySession -Verbose:$false -ErrorAction Stop + $result = Invoke-WebRequest -Uri $meUri -UseDefaultCredentials -SessionVariable mySession -UseBasicParsing -Verbose:$false -ErrorAction Stop } if ($result.StatusCode -ne 200) diff --git a/ReportingServicesTools/Libraries/library.ps1 b/ReportingServicesTools/Libraries/library.ps1 index 4e59f478..f3fd1cf6 100644 --- a/ReportingServicesTools/Libraries/library.ps1 +++ b/ReportingServicesTools/Libraries/library.ps1 @@ -16,12 +16,12 @@ namespace Microsoft.ReportingServicesTools /// /// The name of the Database instance of the Report Server /// - public static string Instance = "MSSQLSERVER"; + public static string Instance = "PBIRS"; /// /// The version of the Report Server /// - public static SqlServerVersion Version = SqlServerVersion.SQLServer2016; + public static SqlServerVersion Version = SqlServerVersion.PowerBIReportServer; /// /// The credentials to use when connecting to the reporting services. @@ -112,12 +112,22 @@ namespace Microsoft.ReportingServicesTools SQLServer2017 = 14, /// - /// SQL Server vNext + /// SQL Server 2019 /// - SQLServervNext = 15 + SQLServer2019 = 15, + + /// + /// SQL Server 2022 + /// + SQLServer2022 = 16, + + /// + /// Power BI Report Server + /// + PowerBIReportServer = 15 } } "@ Try { Add-Type -TypeDefinition $source -ErrorAction Stop } -catch { } +catch { Write-Warning "$_" } diff --git a/ReportingServicesTools/ReportingServicesTools.psd1 b/ReportingServicesTools/ReportingServicesTools.psd1 index e4e4d23f..acd523e8 100644 --- a/ReportingServicesTools/ReportingServicesTools.psd1 +++ b/ReportingServicesTools/ReportingServicesTools.psd1 @@ -5,61 +5,61 @@ @{ # Script module or binary module file associated with this manifest. RootModule = 'ReportingServicesTools.psm1' - + # Version number of this module. - ModuleVersion = '0.0.5.5' - + ModuleVersion = '0.0.9.1' + # ID used to uniquely identify this module GUID = '9d139310-ce45-41ce-8e8b-d76335aa1789' - + # Author of this module Author = 'Microsoft Corporation' - + # Company or vendor of this module CompanyName = 'Microsoft Corporation' - + # Copyright statement for this module Copyright = '2016 Microsoft Corporation' - + # Description of the functionality provided by this module Description = 'Provides extra functionality for SSRS (SQL Server Reporting Services) and Power BI Report Server. Includes the ability to deploy SSRS Reports, Datasets, and DataSources; as well as Power BI Reports. For more information about the capabilities of this module, please visit the Project Site: https://github.com/Microsoft/ReportingServicesTools/' - + # Minimum version of the Windows PowerShell engine required by this module PowerShellVersion = '3.0' - + # Name of the Windows PowerShell host required by this module PowerShellHostName = '' - + # Minimum version of the Windows PowerShell host required by this module PowerShellHostVersion = '' - + # Minimum version of the .NET Framework required by this module DotNetFrameworkVersion = '' - + # Minimum version of the common language runtime (CLR) required by this module CLRVersion = '' - + # Processor architecture (None, X86, Amd64, IA64) required by this module ProcessorArchitecture = '' - + # Modules that must be imported into the global environment prior to importing this module RequiredModules = @() - + # Assemblies that must be loaded prior to importing this module RequiredAssemblies = @() - + # Script files () that are run in the caller's environment prior to importing this module ScriptsToProcess = @() - + # Type files (xml) to be loaded when importing this module TypesToProcess = @() - + # Format files (xml) to be loaded when importing this module FormatsToProcess = @() - + # Modules to import as nested modules of the module specified in ModuleToProcess NestedModules = @() - + # Functions to export from this module FunctionsToExport = @( 'Backup-RsEncryptionKey', @@ -68,15 +68,26 @@ 'Export-RsSubscriptionXml', 'Get-RsCatalogItemRole', 'Get-RsDataSource', + 'Get-RsDeploymentConfig', 'Get-RsFolderContent', + 'Get-RsRestFolderContent', 'Get-RsItemDataSource', 'Get-RsItemReference', + 'Get-RsRestCacheRefreshPlan', + 'Get-RsRestCacheRefreshPlanHistory', + 'Get-RsRestDataSource', 'Get-RsRestItemDataSource', + 'Get-RsRestItemDataModelParameter', + 'Get-RsRestItem', + 'Get-RsRestItemAccessPolicy', + 'Get-RsRestPublicServerSetting', 'Get-RsSubscription', 'Grant-RsCatalogItemRole', + 'Grant-RsRestItemAccessPolicy', 'Grant-RsSystemRole', 'Import-RsSubscriptionXml', - 'Initialize-Rs', + 'Initialize-Rs', + 'New-RsRestCacheRefreshPlan', 'New-RsConfigurationSettingObject', 'New-RsDataSource', 'New-RsFolder', @@ -91,13 +102,16 @@ 'Out-RsFolderContent', 'Out-RsRestCatalogItem', 'Out-RsRestFolderContent', + 'Publish-RsProject', 'Register-RsPowerBI', 'Remove-RsCatalogItem', 'Remove-RsRestFolder', 'Remove-RsRestCatalogItem', 'Remove-RsSubscription', + 'Remove-RsRestCacheRefreshPlan', 'Restore-RsEncryptionKey', 'Revoke-RsCatalogItemAccess', + 'Revoke-RsRestItemAccessPolicy', 'Revoke-RsSystemAccess', 'Set-RsDatabase', 'Set-RsDatabaseCredentials', @@ -107,27 +121,32 @@ 'Set-RsDataSourceReference', 'Set-RsEmailSettings', 'Set-RsItemDataSource', + 'Set-RsRestItemDataModelParameter'; 'Set-RsRestItemDataSource', 'Set-RsSubscription', 'Set-RsUrlReservation', 'Set-PbiRsUrlReservation', + 'Start-RsRestCacheRefreshPlan', + 'Test-RsRestItemDataSource', 'Write-RsCatalogItem', 'Write-RsFolderContent', 'Write-RsRestCatalogItem', 'Write-RsRestFolderContent' ) - + # Cmdlets to export from this module CmdletsToExport = '' - + # Variables to export from this module VariablesToExport = '' - + # Aliases to export from this module # Aliases are stored in ReportingServicesTools.psm1 AliasesToExport = @( 'Get-RsCatalogItems', + 'Get-RsCatalogItem', 'Get-RsChildItem', + 'Get-RsItem', 'Get-RsItemReferences', 'Grant-AccessOnCatalogItem', 'Grant-AccessToRS', @@ -141,13 +160,13 @@ 'Set-RsEmailSettingsAsNTLMAuth', 'Set-RsSharedDataSource' ) - + # List of all modules packaged with this module ModuleList = @() - + # List of all files packaged with this module FileList = '' - + PrivateData = @{ # PSData is module packaging and gallery metadata embedded in PrivateData # It's for rebuilding PowerShellGet (and PoshCode) NuGet-style packages diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 00000000..869fdfe2 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,41 @@ + + +## Security + +Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). + +If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/opensource/security/definition), please report it to us as described below. + +## Reporting Security Issues + +**Please do not report security vulnerabilities through public GitHub issues.** + +Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/opensource/security/create-report). + +If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/opensource/security/pgpkey). + +You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://aka.ms/opensource/security/msrc). + +Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: + + * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) + * Full paths of source file(s) related to the manifestation of the issue + * The location of the affected source code (tag/branch/commit or direct URL) + * Any special configuration required to reproduce the issue + * Step-by-step instructions to reproduce the issue + * Proof-of-concept or exploit code (if possible) + * Impact of the issue, including how an attacker might exploit the issue + +This information will help us triage your report more quickly. + +If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/opensource/security/bounty) page for more details about our active programs. + +## Preferred Languages + +We prefer all communications to be in English. + +## Policy + +Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/opensource/security/cvd). + + diff --git a/Tests/Admin/Rest/Get-RsRestPublicServerSetting.Tests.ps1 b/Tests/Admin/Rest/Get-RsRestPublicServerSetting.Tests.ps1 new file mode 100644 index 00000000..3a4d17ed --- /dev/null +++ b/Tests/Admin/Rest/Get-RsRestPublicServerSetting.Tests.ps1 @@ -0,0 +1,24 @@ +# Copyright (c) 2020 Microsoft Corporation. All Rights Reserved. +# Licensed under the MIT License (MIT) + +Describe "Get-RsRestPublicServerSetting" { + Context "Get Catalog Item Policy"{ + $reportPortalUri = if ($env:PesterPortalUrl -eq $null) { 'http://localhost/reports' } else { $env:PesterPortalUrl } + + It "Should get MaxFileSizeMb property" { + $property = Get-RsRestPublicServerSetting -ReportPortalUri $reportPortalUri -Property "MaxFileSizeMb" + $property | Should -Not -BeNullOrEmpty + $property | Should -Be '1000' + } + + It "Should get ShowDownloadMenu property" { + $property = Get-RsRestPublicServerSetting -ReportPortalUri $reportPortalUri -Property "ShowDownloadMenu" + $property | Should -Not -BeNullOrEmpty + $property | Should -Be 'true' + } + + It "Should not get BadRandomProperty property" { + Get-RsRestPublicServerSetting -ReportPortalUri $reportPortalUri -Property "BadRandomProperty" | Should -BeNullOrEmpty + } + } +} diff --git a/Tests/Admin/Set-RsDatabase.Tests.ps1 b/Tests/Admin/Set-RsDatabase.Tests.ps1 index 8838aa15..702a90c3 100644 --- a/Tests/Admin/Set-RsDatabase.Tests.ps1 +++ b/Tests/Admin/Set-RsDatabase.Tests.ps1 @@ -1,10 +1,10 @@ function Get-DatabaseName() { - $wmiObject = New-RsConfigurationSettingObject -ReportServerInstance PBIRS -ReportServerVersion SQLServervNext + $wmiObject = New-RsConfigurationSettingObject -ReportServerInstance PBIRS -ReportServerVersion PowerBIReportServer return $wmiObject.DatabaseName } function Get-CredentialType() { - $wmiObject = New-RsConfigurationSettingObject -ReportServerInstance PBIRS -ReportServerVersion SQLServervNext + $wmiObject = New-RsConfigurationSettingObject -ReportServerInstance PBIRS -ReportServerVersion PowerBIReportServer switch ($wmiObject.DatabaseLogonType) { 0 { return 'Windows' } 1 { return 'SQL' } @@ -26,7 +26,7 @@ Describe "Set-RsDatabase" { $databaseServerName = 'localhost' $databaseName = 'ReportServer' + [System.DateTime]::Now.Ticks $credentialType = 'ServiceAccount' - Set-RsDatabase -DatabaseServerName $databaseServerName -DatabaseName $databaseName -DatabaseCredentialType $credentialType -Confirm:$false -Verbose -ReportServerInstance PBIRS -ReportServerVersion SQLServervNext + Set-RsDatabase -DatabaseServerName $databaseServerName -DatabaseName $databaseName -DatabaseCredentialType $credentialType -Confirm:$false -Verbose -ReportServerInstance PBIRS -ReportServerVersion PowerBIReportServer It "Should update database and credentials" { Get-DatabaseName | Should be $databaseName @@ -39,7 +39,7 @@ Describe "Set-RsDatabase" { $databaseName = 'ReportServer' + [System.DateTime]::Now.Ticks $credentialType = 'SQL' $credential = Get-SaCredentials - Set-RsDatabase -DatabaseServerName $databaseServerName -DatabaseName $databaseName -DatabaseCredentialType $credentialType -DatabaseCredential $credential -Confirm:$false -Verbose -ReportServerInstance PBIRS -ReportServerVersion SQLServervNext + Set-RsDatabase -DatabaseServerName $databaseServerName -DatabaseName $databaseName -DatabaseCredentialType $credentialType -DatabaseCredential $credential -Confirm:$false -Verbose -ReportServerInstance PBIRS -ReportServerVersion PowerBIReportServer It "Should update database and credentials" { Get-DatabaseName | Should be $databaseName @@ -52,7 +52,7 @@ Describe "Set-RsDatabase" { $databaseName = 'ReportServer' $credentialType = 'SQL' $credential = Get-SaCredentials - Set-RsDatabase -DatabaseServerName $databaseServerName -DatabaseName $databaseName -DatabaseCredentialType $credentialType -DatabaseCredential $credential -IsExistingDatabase -Confirm:$false -Verbose -ReportServerInstance PBIRS -ReportServerVersion SQLServervNext + Set-RsDatabase -DatabaseServerName $databaseServerName -DatabaseName $databaseName -DatabaseCredentialType $credentialType -DatabaseCredential $credential -IsExistingDatabase -Confirm:$false -Verbose -ReportServerInstance PBIRS -ReportServerVersion PowerBIReportServer It "Should update database and credentials" { Get-DatabaseName | Should be $databaseName @@ -64,7 +64,7 @@ Describe "Set-RsDatabase" { $databaseServerName = 'localhost' $databaseName = 'ReportServer' $credentialType = 'ServiceAccount' - Set-RsDatabase -DatabaseServerName $databaseServerName -DatabaseName $databaseName -DatabaseCredentialType $credentialType -IsExistingDatabase -Confirm:$false -Verbose -ReportServerInstance PBIRS -ReportServerVersion SQLServervNext + Set-RsDatabase -DatabaseServerName $databaseServerName -DatabaseName $databaseName -DatabaseCredentialType $credentialType -IsExistingDatabase -Confirm:$false -Verbose -ReportServerInstance PBIRS -ReportServerVersion PowerBIReportServer It "Should update database and credentials" { Get-DatabaseName | Should be $databaseName diff --git a/Tests/Admin/Set-RsDatabaseCredentials.Tests.ps1 b/Tests/Admin/Set-RsDatabaseCredentials.Tests.ps1 index 07cab33a..1021dc1f 100644 --- a/Tests/Admin/Set-RsDatabaseCredentials.Tests.ps1 +++ b/Tests/Admin/Set-RsDatabaseCredentials.Tests.ps1 @@ -1,5 +1,5 @@ function Get-CredentialType() { - $wmiObject = New-RsConfigurationSettingObject -ReportServerInstance PBIRS -ReportServerVersion SQLServervNext + $wmiObject = New-RsConfigurationSettingObject -ReportServerInstance PBIRS -ReportServerVersion PowerBIReportServer switch ($wmiObject.DatabaseLogonType) { 0 { return 'Windows' } 1 { return 'SQL' } @@ -20,7 +20,7 @@ Describe "Set-RsDatabaseCredentials" { Context "Changing database credential type to ServiceAccount credentials" { $credentialType = 'SQL' $credential = Get-SaCredentials - Set-RsDatabaseCredentials -DatabaseCredentialType $credentialType -DatabaseCredential $credential -Confirm:$false -Verbose -ReportServerInstance PBIRS -ReportServerVersion SQLServervNext + Set-RsDatabaseCredentials -DatabaseCredentialType $credentialType -DatabaseCredential $credential -Confirm:$false -Verbose -ReportServerInstance PBIRS -ReportServerVersion PowerBIReportServer It "Should update credentials" { Get-CredentialType | Should be $credentialType @@ -29,7 +29,7 @@ Describe "Set-RsDatabaseCredentials" { Context "Changing database credential type to SQL credentials" { $credentialType = 'ServiceAccount' - Set-RsDatabaseCredentials -DatabaseCredentialType $credentialType -Confirm:$false -Verbose -ReportServerInstance PBIRS -ReportServerVersion SQLServervNext + Set-RsDatabaseCredentials -DatabaseCredentialType $credentialType -Confirm:$false -Verbose -ReportServerInstance PBIRS -ReportServerVersion PowerBIReportServer It "Should update credentials" { Get-CredentialType | Should be $credentialType diff --git a/Tests/CatalogItems/Get-RsDeploymentConfig.Tests.ps1 b/Tests/CatalogItems/Get-RsDeploymentConfig.Tests.ps1 new file mode 100644 index 00000000..e759a3ca --- /dev/null +++ b/Tests/CatalogItems/Get-RsDeploymentConfig.Tests.ps1 @@ -0,0 +1,60 @@ + +Describe "Get-RsDeploymentConfig" { + $RSConfig = Get-RsDeploymentConfig -RsProjectFile "$($PSScriptRoot)\TestProjects\SQLServerPerformanceDashboardReportingSolution\SQL Server Performance Dashboard\SQL Server Performance Dashboard.rptproj" -ConfigurationToUse Release + + Write-Verbose "$RSConfig.TargetServerURL" + Write-Verbose "$RSConfig.RsProjectFolder" + Write-Verbose "$RSConfig.TargetDatasetFolder" + Write-Verbose "$RSConfig.TargetDatasourceFolder" + Write-Verbose "$RSConfig.TargetReportPartFolder" + + Context "Get the Release DeploymentConfig of a ReportServer project file using ConfigurationToUse parameter"{ + It "Should verify TargetServerURL matches" { + $RSConfig.TargetServerURL | Should Be 'http://localhost/reportserver' + } + + It "Should verify a config was retrieved" { + @($RSConfig).Count | Should Be 1 + } + + It "Should verify the Dataset, Datasource, and ReportPart folders are NULL" { + #$RSConfig = Get-RsDeploymentConfig -RsProjectFile "$($PSScriptRoot)\TestProjects\SQLServerPerformanceDashboardReportingSolution\SQL Server Performance Dashboard\SQL Server Performance Dashboard.rptproj" -ConfigurationToUse Release + + Write-Verbose "$RSConfig.TargetDatasetFolder" + Write-Verbose "$RSConfig.TargetDatasourceFolder" + Write-Verbose "$RSConfig.TargetReportPartFolder" + + $RSConfig.TargetDatasetFolder | Should Be '/Datasets' + $RSConfig.TargetDatasourceFolder | Should Be '/Data Sources' + $RSConfig.TargetReportPartFolder | Should Be 'Report Parts' + } + + } + + Context "Get the DebugNull DeploymentConfig of a ReportServer project file using ConfigurationToUse parameter"{ + It "Should verify TargetServerURL matches" { + $RSConfig = Get-RsDeploymentConfig -RsProjectFile "$($PSScriptRoot)\TestProjects\SQLServerPerformanceDashboardReportingSolution\SQL Server Performance Dashboard\SQL Server Performance Dashboard.rptproj" -ConfigurationToUse DebugNull + + Write-Verbose "$RSConfig.TargetServerURL" + Write-Verbose "$RSConfig.RsProjectFolder" + Write-Verbose "$RSConfig.TargetDatasetFolder" + Write-Verbose "$RSConfig.TargetDatasourceFolder" + Write-Verbose "$RSConfig.TargetReportPartFolder" + + $RSConfig.TargetServerURL | Should Be 'http://localhost/reportserver' + } + + It "Should verify a config was retrieved" { + @($RSConfig).Count | Should Be 1 + } + + It "Should verify config was successfully retrieved even though Dataset, Datasource, and ReportPart folders are NULL" { + $RSConfig = Get-RsDeploymentConfig -RsProjectFile "$($PSScriptRoot)\TestProjects\SQLServerPerformanceDashboardReportingSolution\SQL Server Performance Dashboard\SQL Server Performance Dashboard.rptproj" -ConfigurationToUse DebugNull + + $RSConfig.TargetDatasetFolder | Should BeNullOrEmpty + $RSConfig.TargetDatasourceFolder | Should BeNullOrEmpty + $RSConfig.TargetReportPartFolder | Should BeNullOrEmpty + } + + } +} \ No newline at end of file diff --git a/Tests/CatalogItems/Get-RsFolderContentTests.ps1 b/Tests/CatalogItems/Get-RsFolderContentTests.ps1 index 2d4d8dda..384d673c 100644 --- a/Tests/CatalogItems/Get-RsFolderContentTests.ps1 +++ b/Tests/CatalogItems/Get-RsFolderContentTests.ps1 @@ -18,6 +18,8 @@ Describe "Get-RsFolderContent" { # Removing folders used for testing Remove-RsCatalogItem -ReportServerUri 'http://localhost/reportserver' -RsFolder $folderPath -Confirm:$false } +"$($folderName)"; +"$($folderList.Name)"; Context "Get folder with proxy parameter"{ # Create a folder @@ -82,4 +84,7 @@ Describe "Get-RsFolderContent" { # Removing folders used for testing Remove-RsCatalogItem -ReportServerUri 'http://localhost/reportserver' -RsFolder $rootFolderPath -Confirm:$false } +"$($folderName)"; +"$($folderList.Name)"; + } \ No newline at end of file diff --git a/Tests/CatalogItems/New-RsDataSource.Tests.ps1 b/Tests/CatalogItems/New-RsDataSource.Tests.ps1 index 975ba78c..0e863fdc 100644 --- a/Tests/CatalogItems/New-RsDataSource.Tests.ps1 +++ b/Tests/CatalogItems/New-RsDataSource.Tests.ps1 @@ -11,7 +11,7 @@ Function Create-PSCredential ) $SecurePassword = ConvertTo-SecureString $Password -AsPlainText -Force $ps_credential = New-Object System.Management.Automation.PSCredential ($UserName, $SecurePassword) - Return $ps_credential + Return $ps_credential } Function Get-ExistingDataExtension @@ -21,7 +21,7 @@ Function Get-ExistingDataExtension } Describe "New-RsDataSource" { - Context "Create RsDataSource with minimal parameters"{ + Context "Create RsDataSource with minimal parameters" { # Declare datasource Name, Extension, CredentialRetrieval, and DataSource path. $dataSourceName = 'SutDataSourceMinParameters' + [guid]::NewGuid() $extension = Get-ExistingDataExtension @@ -39,7 +39,7 @@ Describe "New-RsDataSource" { Remove-RsCatalogItem -RsFolder $dataSourcePath -Confirm:$false } - Context "Create RsDataSource with ReportServerUri parameter"{ + Context "Create RsDataSource with ReportServerUri parameter" { # Declare datasource Name, Extension, CredentialRetrieval, ReportServerUri and DataSource path. $dataSourceName = 'SutDataSourceReportServerUriParameter' + [guid]::NewGuid() $extension = Get-ExistingDataExtension @@ -54,12 +54,39 @@ Describe "New-RsDataSource" { Remove-RsCatalogItem -RsFolder $dataSourcePath -Confirm:$false } - Context "Create RsDataSource with Proxy parameter"{ + Context "Create RsDataSource with Hidden parameter" { + # Declare datasource Name, Extension, CredentialRetrieval, ReportServerUri and DataSource path. + $dataSourceName = 'SutDataSourceReportServerUriParameter' + [guid]::NewGuid() + $extension = Get-ExistingDataExtension + $credentialRetrieval = 'None' + $reportServerUri = 'http://localhost/reportserver' + $dataSourcePath = '/' + $dataSourceName + New-RsDataSource -RsFolder '/' -Name $dataSourceName -Extension $extension -CredentialRetrieval $credentialRetrieval -ReportServerUri $reportServerUri -Hidden + It "Should be a new data source" { + {Get-RsDataSource -Path $dataSourcePath } | Should not throw + } + + It "Should be hidden" { + #Hidden property is not returned for a datasource. + #(Get-RsDataSource -Path $dataSourcePath).Hidden | Should -BeTrue + + #Get the datasource as folder content + $item=Get-RsFolderContent -RsFolder '/' | Where-Object -Property Name -eq -Value $dataSourceName + $item.Name | Should -Be $dataSourceName + $item.Hidden | Should -BeTrue + } + + + # Removing folders used for testing + Remove-RsCatalogItem -RsFolder $dataSourcePath -Confirm:$false + } + + Context "Create RsDataSource with Proxy parameter" { # Declare datasource Name, Extension, CredentialRetrieval, Proxy and DataSource path. $dataSourceName = 'SutDataSourceProxyParameter' + [guid]::NewGuid() $extension = Get-ExistingDataExtension $credentialRetrieval = 'None' - $proxy = New-RsWebServiceProxy + $proxy = New-RsWebServiceProxy $dataSourcePath = '/' + $dataSourceName New-RsDataSource -RsFolder '/' -Name $dataSourceName -Extension $extension -CredentialRetrieval $credentialRetrieval -Proxy $proxy It "Should be a new data source" { @@ -69,7 +96,7 @@ Describe "New-RsDataSource" { Remove-RsCatalogItem -RsFolder $dataSourcePath -Confirm:$false } - Context "Create RsDataSource with connection string parameter"{ + Context "Create RsDataSource with connection string parameter" { # Declare datasource Name, Extension, CredentialRetrieval, Connection String and DataSource path. $dataSourceName = 'SutDataSourceConnectionStringParameter' + [guid]::NewGuid() $extension = Get-ExistingDataExtension @@ -88,12 +115,12 @@ Describe "New-RsDataSource" { Remove-RsCatalogItem -RsFolder $dataSourcePath -Confirm:$false } - Context "Create RsDataSource with Proxy and ReportServerUri parameters"{ + Context "Create RsDataSource with Proxy and ReportServerUri parameters" { # Declare datasource Name, Extension, CredentialRetrieval, and DataSource path. $dataSourceName = 'SutDataSourceProxyAndReportServerUriParameters' + [guid]::NewGuid() $extension = Get-ExistingDataExtension $credentialRetrieval = 'None' - $proxy = New-RsWebServiceProxy + $proxy = New-RsWebServiceProxy $dataSourcePath = '/' + $dataSourceName $reportServerUri = 'http://localhost/reportserver' New-RsDataSource -RsFolder '/' -Name $dataSourceName -Extension $extension -CredentialRetrieval $credentialRetrieval -Proxy $proxy -ReportServerUri $reportServerUri @@ -104,14 +131,14 @@ Describe "New-RsDataSource" { Remove-RsCatalogItem -RsFolder $dataSourcePath -Confirm:$false } - Context "Create RsDataSource with unsupported RsDataSource Extension validation"{ + Context "Create RsDataSource with unsupported RsDataSource Extension validation" { # Declare datasource Name, Extension, CredentialRetrieval, and DataSource path. $dataSourceName = 'SutDataSurceExtensionException' + [guid]::NewGuid() $extension = 'InvalidExtension' $credentialRetrieval = 'Integrated' $dataSourcePath = '/' + $dataSourceName It "Should throw an exception when datasource is failed to be create" { - { New-RsDataSource -RsFolder '/' -Name $dataSourceName -Extension $extension -CredentialRetrieval $credentialRetrieval } | Should throw + { New-RsDataSource -RsFolder '/' -Name $dataSourceName -Extension $extension -CredentialRetrieval $credentialRetrieval } | Should throw { Get-RsDataSource -Path $dataSourcePath } | Should throw } } @@ -123,7 +150,7 @@ Describe "New-RsDataSource" { $credentialRetrieval = 'Store' $dataSourcePath = '/' + $dataSourceName It "Should throw an exception when Store credential retrieval are given without providing credential" { - { New-RsDataSource -RsFolder '/' -Name $dataSourceName -Extension $extension -CredentialRetrieval $credentialRetrieval } | Should throw + { New-RsDataSource -RsFolder '/' -Name $dataSourceName -Extension $extension -CredentialRetrieval $credentialRetrieval } | Should throw { Get-RsDataSource -Path $dataSourcePath } | Should throw } } @@ -157,7 +184,7 @@ Describe "New-RsDataSource" { # $password ='MyPassword' # $userName = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name # $dataSourceCredentials = Create-PSCredential -User $userName -Password $password - # New-RsDataSource -RsFolder '/' -Name $dataSourceName -Extension $extension -CredentialRetrieval $credentialRetrieval -DatasourceCredentials $dataSourceCredentials -ImpersonateUser + # New-RsDataSource -RsFolder '/' -Name $dataSourceName -Extension $extension -CredentialRetrieval $credentialRetrieval -DatasourceCredentials $dataSourceCredentials -ImpersonateUser # $dataSource = Get-RsDataSource -Path $dataSourcePath # It "Should be a new data source" { # $dataSource.Count | Should Be 1 @@ -167,7 +194,7 @@ Describe "New-RsDataSource" { # Remove-RsCatalogItem -RsFolder $dataSourcePath -Confirm:$false # } - Context "Create RsDataSource with Windows Credentials Parameter"{ + Context "Create RsDataSource with Windows Credentials Parameter" { # Declare datasource Name, Extension, CredentialRetrieval, and DataSource path. $dataSourceName = 'SutDataSurceWindowsCredentials' + [guid]::NewGuid() $extension = Get-ExistingDataExtension @@ -188,7 +215,7 @@ Describe "New-RsDataSource" { Remove-RsCatalogItem -RsFolder $dataSourcePath -Confirm:$false } - Context "Create RsDataSource with Prompt Credentials Retrieval"{ + Context "Create RsDataSource with Prompt Credentials Retrieval" { # Declare datasource Name, Extension, CredentialRetrieval (Prompt), and DataSource path. $dataSourceName = 'SutDataSurcePrompt' + [guid]::NewGuid() $extension = Get-ExistingDataExtension @@ -203,7 +230,7 @@ Describe "New-RsDataSource" { Remove-RsCatalogItem -RsFolder $dataSourcePath -Confirm:$false } - Context "Create RsDataSource and Overwrite it"{ + Context "Create RsDataSource and Overwrite it" { # Declare datasource name, extension, credential retrieval, and data source path. $dataSourceName = 'SutDataSourceOverwrite' + [guid]::NewGuid() $extension = 'SQL' @@ -215,14 +242,14 @@ Describe "New-RsDataSource" { New-RsDataSource -RsFolder '/' -Name $dataSourceName -Extension $extension -CredentialRetrieval $credentialRetrievalChange -Overwrite $dataSource = Get-RsDataSource -Path $dataSourcePath It "Should overwrite a datasource" { - $dataSource.CredentialRetrieval | Should be $credentialRetrievalChange + $dataSource.CredentialRetrieval | Should be $credentialRetrievalChange $dataSource.Count | Should Be 1 } # Removing folders used for testing Remove-RsCatalogItem -RsFolder $dataSourcePath -Confirm:$false } - Context "Create RsDataSource with description"{ + Context "Create RsDataSource with description" { # Declare datasource Name, Extension, CredentialRetrieval, and DataSource path. $dataSourceName = 'SutDataSourceDescription' + [guid]::NewGuid() $extension = Get-ExistingDataExtension @@ -240,9 +267,9 @@ Describe "New-RsDataSource" { $descriptionProperty = $properties | Where { $_.Name -eq 'Description' } $descriptionProperty | Should Not BeNullOrEmpty $descriptionProperty.Value | Should Be $description - + } # Removing folders used for testing Remove-RsCatalogItem -RsFolder $dataSourcePath -Confirm:$false } -} \ No newline at end of file +} diff --git a/Tests/CatalogItems/Publish-RsProject.Test.ps1 b/Tests/CatalogItems/Publish-RsProject.Test.ps1 new file mode 100644 index 00000000..a5324808 --- /dev/null +++ b/Tests/CatalogItems/Publish-RsProject.Test.ps1 @@ -0,0 +1,59 @@ +$reportPortalUri = if ($env:PesterPortalUrl -eq $null) { 'http://localhost/reports' } else { $env:PesterPortalUrl } + +Describe "Publish-RsProject" { + Context "Deploy an entire SSRS Project by getting the Release DeploymentConfig of a ReportServer project file using ConfigurationToUse parameter"{ + # Create a folder + $RSConfig = Get-RsDeploymentConfig -RsProjectFile "$($PSScriptRoot)\TestProjects\SQLServerPerformanceDashboardReportingSolution\SQL Server Performance Dashboard\SQL Server Performance Dashboard.rptproj" -ConfigurationToUse Release | + Add-Member -PassThru -MemberType NoteProperty -Name ReportPortal -Value $reportPortalUri + + # Test if the config was retrieved + It "Should verify a config was retrieved" { + @($RSConfig).Count | Should Be 1 + } + + # Test if the TargetServerURL in the config was found + It "Should verify TargetServerURL matches" { + $RSConfig.TargetServerURL | Should Be 'http://localhost/reportserver' + } + + $RSConfig | Publish-RsProject + $CatalogList = Get-RsRestFolderContent -reportPortalUri $RSConfig.ReportPortal -RsFolder '/SQL Server Performance Dashboard' -Recurse + $folderCount = ($CatalogList | measure).Count + It "Should find 22 reports inside the folder" { + $folderCount | Should Be 22 + } + # Removing folders used for testing + Remove-RsCatalogItem -RsFolder $RSConfig.TargetReportFolder -Confirm:$false + Remove-RsCatalogItem -RsFolder $RSConfig.TargetDatasetFolder -Confirm:$false + Remove-RsCatalogItem -RsFolder $RSConfig.TargetDatasourceFolder -Confirm:$false + + } + + Context "Deploy an entire SSRS Project by getting the Debug DeploymentConfig of a ReportServer project file using ConfigurationToUse parameter"{ + # Create a folder + $RSConfig = Get-RsDeploymentConfig -RsProjectFile "$($PSScriptRoot)\TestProjects\SQLServerPerformanceDashboardReportingSolution\SQL Server Performance Dashboard\SQL Server Performance Dashboard.rptproj" -ConfigurationToUse Debug | + Add-Member -PassThru -MemberType NoteProperty -Name ReportPortal -Value $reportPortalUri + + # Test if the config was retrieved + It "Should verify a config was retrieved" { + @($RSConfig).Count | Should Be 1 + } + + # Test if the TargetServerURL in the config was found + It "Should verify TargetServerURL matches" { + $RSConfig.TargetServerURL | Should Be 'http://localhost/reportserver' + } + + $RSConfig | Publish-RsProject + $CatalogList = Get-RsRestFolderContent -reportPortalUri $RSConfig.ReportPortal -RsFolder '/SQL Server Performance Dashboard' -Recurse + $folderCount = ($CatalogList | measure).Count + It "Should find 22 reports inside the folder" { + $folderCount | Should Be 22 + } + # Removing folders used for testing + Remove-RsCatalogItem -RsFolder "/$($RSConfig.TargetReportFolder)" -Confirm:$false + Remove-RsCatalogItem -RsFolder "/$($RSConfig.TargetDatasetFolder)" -Confirm:$false + Remove-RsCatalogItem -RsFolder "/$($RSConfig.TargetDatasourceFolder)" -Confirm:$false + + } +} \ No newline at end of file diff --git a/Tests/CatalogItems/Rest/Get-RsRestCacheRefreshPlan.Tests.ps1 b/Tests/CatalogItems/Rest/Get-RsRestCacheRefreshPlan.Tests.ps1 new file mode 100644 index 00000000..9ae20f55 --- /dev/null +++ b/Tests/CatalogItems/Rest/Get-RsRestCacheRefreshPlan.Tests.ps1 @@ -0,0 +1,73 @@ +# Copyright (c) 2021 Microsoft Corporation. All Rights Reserved. +# Licensed under the MIT License (MIT) + +$reportPortalUri = if ($env:PesterPortalUrl -eq $null) { 'http://localhost/reports' } else { $env:PesterPortalUrl } +$reportServerUri = if ($env:PesterServerUrl -eq $null) { 'http://localhost/reportserver' } else { $env:PesterServerUrl } + +function VerifyCatalogItemExists() +{ + param( + [Parameter(Mandatory = $True)] + [string] + $itemName, + + [Parameter(Mandatory = $True)] + [string] + $itemType, + + [Parameter(Mandatory = $True)] + [string] + $folderPath, + + [string] + $reportPortalUri + ) + + $item = (Get-RsRestItem -reportPortalUri $reportPortalUri -RsItem "$($folderPath)/$($itemName)" ) | Where-Object { $_.Type -eq $itemType -and $_.Name -eq $itemName } + $item | Should Not BeNullOrEmpty +} + +Describe "Get-RsRestCacheRefreshPlan" { + $rsFolderPath = "" + $localPath = (Get-Item -Path ".\").FullName + '\Tests\CatalogItems\testResources' + + BeforeEach { + $folderName = 'SUT_WriteRsRestCatalogItem_' + [guid]::NewGuid() + New-RsRestFolder -ReportPortalUri $reportPortalUri -RsFolder / -FolderName $folderName -Verbose + $rsFolderPath = '/' + $folderName + $itemPath = $localPath + '\ReportCatalog.pbix' + Write-RsRestCatalogItem -ReportPortalUri $reportPortalUri -Path $itemPath -RsFolder $rsFolderPath -Verbose + VerifyCatalogItemExists -itemName 'ReportCatalog' -itemType 'PowerBIReport' -folderPath $rsFolderPath -reportPortalUri $reportPortalUri + + $dataSources = Get-RsRestItemDataSource -RsItem "$($rsFolderPath)/ReportCatalog" -ReportPortalUri $reportPortalUri + $dataSources[0].DataModelDataSource.AuthType = 'UsernamePassword' + $dataSources[0].DataModelDataSource.Username = 'PBIRS' + $dataSources[0].DataModelDataSource.Secret = 'password' + Set-RsRestItemDataSource -RsItem "$($rsFolderPath)/ReportCatalog" -ReportPortalUri $reportPortalUri -DataSources $dataSources -RsItemType 'PowerBIReport' + New-RsRestCacheRefreshPlan -RsItem "$($rsFolderPath)/ReportCatalog" -ReportPortalUri $reportPortalUri -Description 'My New Refresh Plan' -Verbose + + } + + AfterEach { + Remove-RsRestCatalogItem -ReportPortalUri $reportPortalUri -RsItem $rsFolderPath -Confirm:$false + } + + Context "ReportPortalUri parameter" { + + It "Should retrieve a CacheRefreshPlan for a PBIX report" { + + $plan = Get-RsRestCacheRefreshPlan -ReportPortalUri $reportPortalUri -RsReport "$($rsFolderPath)/ReportCatalog" + $plan | Should Not BeNullOrEmpty + $plan.LastStatus | Should -Be 'New Scheduled Refresh Plan' + } + + It "Should retrieve two CacheRefreshPlans for a PBIX report" { + New-RsRestCacheRefreshPlan -RsItem "$($rsFolderPath)/ReportCatalog" -ReportPortalUri $reportPortalUri -Description 'My Other Refresh Plan' -Verbose + + $plans = Get-RsRestCacheRefreshPlan -ReportPortalUri $reportPortalUri -RsReport "$($rsFolderPath)/ReportCatalog" + $plans | Should Not BeNullOrEmpty + $plans.LastStatus | Should -Be @('New Scheduled Refresh Plan', 'New Scheduled Refresh Plan') + $plans | ft -AutoSize + } + } +} diff --git a/Tests/CatalogItems/Rest/Get-RsRestCacheRefreshPlanHistory.Tests.ps1 b/Tests/CatalogItems/Rest/Get-RsRestCacheRefreshPlanHistory.Tests.ps1 new file mode 100644 index 00000000..5cdd5021 --- /dev/null +++ b/Tests/CatalogItems/Rest/Get-RsRestCacheRefreshPlanHistory.Tests.ps1 @@ -0,0 +1,84 @@ +# Copyright (c) 2021 Microsoft Corporation. All Rights Reserved. +# Licensed under the MIT License (MIT) + +$reportPortalUri = if ($env:PesterPortalUrl -eq $null) { 'http://localhost/reports' } else { $env:PesterPortalUrl } +$reportServerUri = if ($env:PesterServerUrl -eq $null) { 'http://localhost/reportserver' } else { $env:PesterServerUrl } + +function VerifyCatalogItemExists() +{ + param( + [Parameter(Mandatory = $True)] + [string] + $itemName, + + [Parameter(Mandatory = $True)] + [string] + $itemType, + + [Parameter(Mandatory = $True)] + [string] + $folderPath, + + [string] + $reportPortalUri + ) + + $item = (Get-RsRestItem -reportPortalUri $reportPortalUri -RsItem "$($folderPath)/$($itemName)" ) | Where-Object { $_.Type -eq $itemType -and $_.Name -eq $itemName } + $item | Should Not BeNullOrEmpty +} + +Describe "Get-RsRestCacheRefreshPlanHistory" { + $rsFolderPath = "" + $localPath = (Get-Item -Path ".\").FullName + '\Tests\CatalogItems\testResources' + + BeforeEach { + $folderName = 'SUT_GetRsRestCacheRefreshPlanHistory_' + [guid]::NewGuid() + New-RsRestFolder -ReportPortalUri $reportPortalUri -RsFolder / -FolderName $folderName -Verbose + $rsFolderPath = '/' + $folderName + $itemPath = $localPath + '\ReportCatalog.pbix' + Write-RsRestCatalogItem -ReportPortalUri $reportPortalUri -Path $itemPath -RsFolder $rsFolderPath -Verbose + VerifyCatalogItemExists -itemName 'ReportCatalog' -itemType 'PowerBIReport' -folderPath $rsFolderPath -reportPortalUri $reportPortalUri + + $dataSources = Get-RsRestItemDataSource -RsItem "$($rsFolderPath)/ReportCatalog" -ReportPortalUri $reportPortalUri + $dataSources[0].DataModelDataSource.AuthType = 'UsernamePassword' + $dataSources[0].DataModelDataSource.Username = 'sa' + $dataSources[0].DataModelDataSource.Secret = 'i<3ReportingServices' + Set-RsRestItemDataSource -RsItem "$($rsFolderPath)/ReportCatalog" -ReportPortalUri $reportPortalUri -DataSources $dataSources -RsItemType 'PowerBIReport' + New-RsRestCacheRefreshPlan -RsItem "$($rsFolderPath)/ReportCatalog" -ReportPortalUri $reportPortalUri -Description 'My New Refresh Plan' -Verbose + + } + + AfterEach { + Remove-RsRestCatalogItem -ReportPortalUri $reportPortalUri -RsItem $rsFolderPath -Confirm:$false + } + + Context "ReportPortalUri parameter" { + + It "Should retrieve history of all CacheRefreshPlan plan for a PBIX report" { + Start-RsRestCacheRefreshPlan -ReportPortalUri $reportPortalUri -RsReport "$($rsFolderPath)/ReportCatalog" + + $plan = Get-RsCacheRefreshPlan -ReportPortalUri $reportPortalUri -RsReport "$($rsFolderPath)/ReportCatalog" + $plan | Should Not BeNullOrEmpty + $plan.LastStatus | Should Not Be 'New Scheduled Refresh Plan' + + $timer= get-date + while ((@($someHistory).count -lt 1)) { + $someHistory = Get-RsRestCacheRefreshPlanHistory -ReportPortalUri $reportPortalUri -RsReport "$($rsFolderPath)/ReportCatalog" + if(((get-date)-$timer).seconds -ge 15) { + break + } + } + @($someHistory).count | Should be 1 + + Start-RsRestCacheRefreshPlan -ReportPortalUri $reportPortalUri -RsReport "$($rsFolderPath)/ReportCatalog" + $timer= get-date + while ((@($moreHistory).count -lt 2)) { + $moreHistory = Get-RsRestCacheRefreshPlanHistory -ReportPortalUri $reportPortalUri -RsReport "$($rsFolderPath)/ReportCatalog" + if(((get-date)-$timer).seconds -ge 15) { + break + } + } + @($moreHistory).count | Should be 2 + } + } +} diff --git a/Tests/CatalogItems/Rest/Get-RsRestDataSource.Tests.ps1 b/Tests/CatalogItems/Rest/Get-RsRestDataSource.Tests.ps1 new file mode 100644 index 00000000..1219b97d --- /dev/null +++ b/Tests/CatalogItems/Rest/Get-RsRestDataSource.Tests.ps1 @@ -0,0 +1,35 @@ +# Copyright (c) 2023 Microsoft Corporation. All Rights Reserved. +# Licensed under the MIT License (MIT) + +$reportPortalUri = if ($null -eq $env:PesterPortalUrl) { 'http://localhost/reports' } else { $env:PesterPortalUrl } + +Describe "Get-RsRestDataSource" { + BeforeAll { + $folderName = 'SutGetRsRestDataSource_' + [guid]::NewGuid() + $dsName = 'SutWriteRsFolderContent_DataSource' + $dsPath = "/$folderName/$dsName" + $localPath = (Get-Item -Path ".\").FullName + '\Tests\CatalogItems\testResources' + $dsLocalPath = $localPath + "\$dsName.rsds" + New-RsRestFolder -ReportPortalUri $reportPortalUri -RsFolder '/' -Name $folderName + Write-RsRestCatalogItem -ReportPortalUri $reportPortalUri -Path $dsLocalPath -RsFolder "/$folderName" -Overwrite + } + AfterAll { + Remove-RsRestCatalogItem -ReportPortalUri $reportPortalUri -RsItem $dsPath -Confirm:$false + Remove-RsRestFolder -ReportPortalUri $reportPortalUri -RsFolder "/$folderName" -Confirm:$false + } + + Context "Datasource exists" { + It "Should retrieve a datasource" { + $ds = Get-RsRestDataSource -ReportPortalUri $reportPortalUri -RsItem $dsPath + $ds.Name | Should -Be $dsName + $ds.Type | Should -Be 'DataSource' + $ds.ConnectionString | Should -Not -BeNullOrEmpty + } + } + Context "Datasource does not exist" { + It "Should throw" { + $result = { Get-RsRestDataSource -ReportPortalUri $reportPortalUri -RsItem '/missing' } | Should -Throw -ExpectedMessage '/missing' -Because 'the invalid path was "/missing"' -PassThru + $result.Exception.Message | Should -BeLike '*/missing*' + } + } +} \ No newline at end of file diff --git a/Tests/CatalogItems/Rest/Get-RsRestFolderContent.Tests.ps1 b/Tests/CatalogItems/Rest/Get-RsRestFolderContent.Tests.ps1 new file mode 100644 index 00000000..f5c6f64d --- /dev/null +++ b/Tests/CatalogItems/Rest/Get-RsRestFolderContent.Tests.ps1 @@ -0,0 +1,85 @@ +# Copyright (c) 2020 Microsoft Corporation. All Rights Reserved. +# Licensed under the MIT License (MIT) + +$reportPortalUri = if ($env:PesterPortalUrl -eq $null) { 'http://localhost/reports' } else { $env:PesterPortalUrl } +$reportServerUri = if ($env:PesterServerUrl -eq $null) { 'http://localhost/reportserver' } else { $env:PesterServerUrl } + +Describe "Get-RsRestFolderContent" { + Context "Get folder with reportPortalUri parameter"{ + # Create a folder + $folderName = 'SutGetFolderReportPortalUriParameter' + [guid]::NewGuid() + New-RsRestFolder -reportPortalUri $reportPortalUri -RsFolder / -FolderName $folderName + $folderPath = '/' + $folderName + # Test if the folder can be found + $folderList = Get-RsRestFolderContent -reportPortalUri $reportPortalUri -RsFolder / + $folderCount = ($folderList | Where-Object name -eq $folderName | measure).Count + It "Should found a folder" { + $folderCount | Should Be 1 + } + Write-Host "---------------------------- + " + Write-Host "Showing $folderName variable for single folder test + " + Write-Host "$folderName" + Write-Host "---------------------------- + " + Write-Host "Showing $folderList variable for single folder test + " + Write-Host "$folderList" + Write-Host "---------------------------- + " + Write-Host "Showing $folderCount variable for single folder test + " + Write-Host "$folderCount" + Write-Host "---------------------------- + " + # Removing folders used for testing + Remove-RsRestCatalogItem -ReportPortalUri $reportPortalUri -RsItem $folderPath -Confirm:$false + } + Context "Get folder inside 4 folders"{ + # Create the first folder in the root + $sutRootFolder = 'SutGetFolderParent' + [guid]::NewGuid() + New-RsRestFolder -reportPortalUri $reportPortalUri -RsFolder / -FolderName $sutRootFolder + # Create 5 folders, one inside the other + $currentFolderDepth = 2 + $folderParentName = $sutRootFolder + While ($currentFolderDepth -le 5) + { + # Create a folder in a specified path + $folderParentPath += '/' + $folderParentName + $folderParentName = 'SutGetFolderParent' + $currentFolderDepth + New-RsRestFolder -reportPortalUri $reportPortalUri -RsFolder $folderParentPath -FolderName $folderParentName + $currentFolderDepth +=1 + + } + # Test if the ´SutGetFolderParent5´ folder inside the other folders can be found + $fifthFolderPath = $folderParentPath + '/' + $folderParentName + $rootFolderPath = '/' + $sutRootFolder + $folderList = Get-RsRestFolderContent -reportPortalUri $reportPortalUri -RsFolder $rootFolderPath -Recurse + $folderCount = ($folderList | Where-Object path -eq $fifthFolderPath | measure).Count + It "Should found 4 subfolders" { + $folderCount | Should Be 1 + $folderList.Count | Should be 4 + + Write-Host "---------------------------- + " + Write-Host "Showing $folderName variable for 4 folders test + " + Write-Host "$folderName" + Write-Host "---------------------------- + " + Write-Host "Showing $folderList variable for 4 folders test + " + Write-Host "$folderList" + Write-Host "---------------------------- + " + Write-Host "Showing $folderCount variable for 4 folders test + " + Write-Host "$folderCount" + Write-Host "---------------------------- + " + } + # Removing folders used for testing + Remove-RsRestCatalogItem -ReportPortalUri $reportPortalUri -RsItem $rootFolderPath -Confirm:$false + } +} \ No newline at end of file diff --git a/Tests/CatalogItems/Rest/Get-RsRestItem.Tests.ps1 b/Tests/CatalogItems/Rest/Get-RsRestItem.Tests.ps1 new file mode 100644 index 00000000..da0bfe7d --- /dev/null +++ b/Tests/CatalogItems/Rest/Get-RsRestItem.Tests.ps1 @@ -0,0 +1,86 @@ +# Copyright (c) 2020 Microsoft Corporation. All Rights Reserved. +# Licensed under the MIT License (MIT) + +$reportPortalUri = if ($env:PesterPortalUrl -eq $null) { 'http://localhost/reports' } else { $env:PesterPortalUrl } +$reportServerUri = if ($env:PesterServerUrl -eq $null) { 'http://localhost/reportserver' } else { $env:PesterServerUrl } + +Describe "Get-RsRestItem" { + Context "Get folder with reportPortalUri parameter"{ + # Create a folder + $folderName = 'SutGetFolderReportPortalUriParameter' + [guid]::NewGuid() + New-RsRestFolder -reportPortalUri $reportPortalUri -RsFolder / -FolderName $folderName + $folderPath = '/' + $folderName + # Test if the folder can be found + $folderList = Get-RsRestItem -reportPortalUri $reportPortalUri -RsItem $folderPath + $folderCount = $folderList | Where-Object name -eq $folderName | measure + It "Should found a folder" { + $folderCount.Count | Should Be 1 + } + # Removing folders used for testing + Remove-RsRestCatalogItem -ReportPortalUri $reportPortalUri -RsItem $folderPath -Confirm:$false + } +Write-Host '---------------------------- +' +Write-Host 'Showing $folderName variable for 4 folders test +' +Write-Host "$($folderName)" +Write-Host '---------------------------- +' +Write-Host 'Showing $folderList variable for 4 folders test +' +Write-Host "$($folderList)" +Write-Host '---------------------------- +' +Write-Host 'Showing $folderCount variable for 4 folders test +' +Write-Host "$($folderCount)" +Write-Host '---------------------------- +' + + Context "Get folder inside 4 folders"{ + # Create the first folder in the root + $sutRootFolder = 'SutGetFolderParent' + [guid]::NewGuid() + New-RsRestFolder -reportPortalUri $reportPortalUri -RsFolder / -FolderName $sutRootFolder + # Create 5 folders, one inside the other + $currentFolderDepth = 2 + $folderParentName = $sutRootFolder + While ($currentFolderDepth -le 5) + { + # Create a folder in a specified path + $folderParentPath += '/' + $folderParentName + $folderParentName = 'SutGetFolderParent' + $currentFolderDepth + New-RsRestFolder -reportPortalUri $reportPortalUri -RsFolder $folderParentPath -FolderName $folderParentName + $currentFolderDepth +=1 + + } + # Test if the ´SutGetFolderParent5´ folder inside the other folders can be found + $fifthFolderPath = $folderParentPath + '/' + $folderParentName + $rootFolderPath = '/' + $sutRootFolder + $folderList = Get-RsRestItem -reportPortalUri $reportPortalUri -RsItem "$folderParentPath/$folderParentName" + $folderCount = $folderList | Where-Object path -eq $fifthFolderPath | measure + It "Should find 1 subfolder underneath 4 subfolders" { + $folderCount.Count | Should Be 1 + ($folderList | measure).count | Should be 1 + } + # Removing folders used for testing + Remove-RsRestCatalogItem -ReportPortalUri $reportPortalUri -RsItem $rootFolderPath -Confirm:$false + Write-Host '---------------------------- + ' + Write-Host 'Showing $folderName variable for 4 folders test + ' + Write-Host "$($folderName)" + Write-Host '---------------------------- + ' + Write-Host 'Showing $folderList variable for 4 folders test + ' + Write-Host "$($folderList)" + Write-Host '---------------------------- + ' + Write-Host 'Showing $folderCount variable for 4 folders test + ' + Write-Host "$($folderCount)" + Write-Host '---------------------------- + ' + } + +} \ No newline at end of file diff --git a/Tests/CatalogItems/Rest/Get-RsRestItemDataModelParameters.Tests.ps1 b/Tests/CatalogItems/Rest/Get-RsRestItemDataModelParameters.Tests.ps1 new file mode 100644 index 00000000..7c5bf65a --- /dev/null +++ b/Tests/CatalogItems/Rest/Get-RsRestItemDataModelParameters.Tests.ps1 @@ -0,0 +1,52 @@ +# Copyright (c) 2017 Microsoft Corporation. All Rights Reserved. +# Licensed under the MIT License (MIT) + +$reportPortalUri = if ($env:PesterPortalUrl -eq $null) { 'http://localhost/reports' } else { $env:PesterPortalUrl } + +Describe "Get-RsRestItemDataModelParameter" { + $session = $null + $rsFolderPath = "" + $sqlPowerBIReport = "" + + BeforeEach { + $session = New-RsRestSession -ReportPortalUri $reportPortalUri + + # creating test folder + $folderName = 'SUT_GetRsRestItemDataModelParameters_' + [guid]::NewGuid() + New-RsRestFolder -WebSession $session -RsFolder / -FolderName $folderName + $rsFolderPath = '/' + $folderName + + # uploading test artifacts: dataModelParametersReport.rdl and DataModelParameters.pbix + $localPath = (Get-Item -Path ".\").FullName + '\Tests\CatalogItems\testResources' + + Write-RsRestCatalogItem -WebSession $session -Path "$localPath\DataModelParameters.pbix" -RsFolder $rsFolderPath + $sqlPowerBIReport = "$rsFolderPath/DataModelParameters" + } + + AfterEach { + # deleting test folder + Remove-RsRestFolder -WebSession $session -RsFolder $rsFolderPath -Confirm:$false + } + + Context "ReportPortalUri parameter" { + It "fetches parameters for power bi reports" { + $dataModelParameters = Get-RsRestItemDataModelParameter -ReportPortalUri $reportPortalUri -RsItem $sqlPowerBIReport -Verbose + $dataModelParameters[0].Name | Should Be "Databasename" + $dataModelParameters[0].Value | Should Be "ReportServer_2019" + } + } + + Context "WebSession parameter" { + $rsSession = $null + + BeforeEach { + $rsSession = New-RsRestSession -ReportPortalUri $reportPortalUri + } + + It "fetches data sources for power bi reports" { + $dataModelParameters = Get-RsRestItemDataModelParameter -WebSession $rsSession -RsItem $sqlPowerBIReport -Verbose + $dataModelParameters[0].Name | Should Be "Databasename" + $dataModelParameters[0].Value | Should Be "ReportServer_2019" + } + } +} \ No newline at end of file diff --git a/Tests/CatalogItems/Rest/New-RsRestCacheRefreshPlan.Tests.ps1 b/Tests/CatalogItems/Rest/New-RsRestCacheRefreshPlan.Tests.ps1 new file mode 100644 index 00000000..a79907ef --- /dev/null +++ b/Tests/CatalogItems/Rest/New-RsRestCacheRefreshPlan.Tests.ps1 @@ -0,0 +1,74 @@ +# Copyright (c) 2021 Microsoft Corporation. All Rights Reserved. +# Licensed under the MIT License (MIT) + +$reportPortalUri = if ($env:PesterPortalUrl -eq $null) { 'http://localhost/reports' } else { $env:PesterPortalUrl } +$reportServerUri = if ($env:PesterServerUrl -eq $null) { 'http://localhost/reportserver' } else { $env:PesterServerUrl } + +function VerifyCatalogItemExists() +{ + param( + [Parameter(Mandatory = $True)] + [string] + $itemName, + + [Parameter(Mandatory = $True)] + [string] + $itemType, + + [Parameter(Mandatory = $True)] + [string] + $folderPath, + + [string] + $reportPortalUri + ) + + $item = (Get-RsRestItem -reportPortalUri $reportPortalUri -RsItem "$($folderPath)/$($itemName)" ) | Where-Object { $_.Type -eq $itemType -and $_.Name -eq $itemName } + $item | Should Not BeNullOrEmpty +} + +Describe "New-RsRestCacheRefreshPlan" { + $rsFolderPath = "" + $localPath = (Get-Item -Path ".\").FullName + '\Tests\CatalogItems\testResources' + + BeforeEach { + $folderName = 'SUT_WriteRsRestCatalogItem_' + [guid]::NewGuid() + New-RsRestFolder -ReportPortalUri $reportPortalUri -RsFolder / -FolderName $folderName -Verbose + $rsFolderPath = '/' + $folderName + $itemPath = $localPath + '\ReportCatalog.pbix' + Write-RsRestCatalogItem -ReportPortalUri $reportPortalUri -Path $itemPath -RsFolder $rsFolderPath -Verbose + VerifyCatalogItemExists -itemName 'ReportCatalog' -itemType 'PowerBIReport' -folderPath $rsFolderPath -reportPortalUri $reportPortalUri + + $dataSources = Get-RsRestItemDataSource -RsItem "$($rsFolderPath)/ReportCatalog" -ReportPortalUri $reportPortalUri + $dataSources[0].DataModelDataSource.AuthType = 'UsernamePassword' + $dataSources[0].DataModelDataSource.Username = 'PBIRS' + $dataSources[0].DataModelDataSource.Secret = 'password' + Set-RsRestItemDataSource -RsItem "$($rsFolderPath)/ReportCatalog" -ReportPortalUri $reportPortalUri -DataSources $dataSources -RsItemType 'PowerBIReport' + + } + + AfterEach { + Remove-RsRestCatalogItem -ReportPortalUri $reportPortalUri -RsItem $rsFolderPath -Confirm:$false + } + + Context "ReportPortalUri parameter" { + + It "Should add a CacheRefreshPlan plan for a PBIX report" { + New-RsRestCacheRefreshPlan -RsItem "$($rsFolderPath)/ReportCatalog" -ReportPortalUri $reportPortalUri -Description 'My New Refresh Plan' -Verbose + + $plan = Get-RsCacheRefreshPlan -ReportPortalUri $reportPortalUri -RsReport "$($rsFolderPath)/ReportCatalog" + $plan | Should Not BeNullOrEmpty + } + + It "Should add a CacheRefreshPlan plan for a PBIX report" { + New-RsRestCacheRefreshPlan -RsItem "$($rsFolderPath)/ReportCatalog" -ReportPortalUri $reportPortalUri -StartDateTime "2021-01-07T06:00:00-00:00" -Recurrence @{ + "DailyRecurrence" = @{ + "DaysInterval" = "1"; + } + } -Verbose + + $plan = Get-RsCacheRefreshPlan -ReportPortalUri $reportPortalUri -RsReport "$($rsFolderPath)/ReportCatalog" + $plan[0].Schedule.Definition.StartDateTime | Should Be @('2021-01-07T06:00:00Z') + } + } +} diff --git a/Tests/CatalogItems/Rest/Out-RsRestCatalogItem.Tests.ps1 b/Tests/CatalogItems/Rest/Out-RsRestCatalogItem.Tests.ps1 index 8588ec16..0981b0b6 100644 --- a/Tests/CatalogItems/Rest/Out-RsRestCatalogItem.Tests.ps1 +++ b/Tests/CatalogItems/Rest/Out-RsRestCatalogItem.Tests.ps1 @@ -44,7 +44,10 @@ Describe "Out-RsRestCatalogItem" { AfterEach { Remove-RsCatalogItem -ReportServerUri $reportServerUri -RsFolder $rsFolderPath -Confirm:$false - Remove-Item -Path $localFolderPath -Recurse + if ($localFolderPath -ne $null -and $localFolderPath.Length -ne 0 -and (Test-Path $localFolderPath)) + { + Remove-Item -Path $localFolderPath -Recurse + } } Context "ReportPortalUri parameter" { @@ -66,12 +69,6 @@ Describe "Out-RsRestCatalogItem" { VerifyFileWasDownloaded -folderPath $localFolderPath -fileName 'SutWriteRsFolderContent_DataSource.rsds' } - It "Should download a RSMOBILE file" { - $itemPath = Join-Path -Path $rsFolderPath -ChildPath SimpleMobileReport - Out-RsRestCatalogItem -ReportPortalUri $reportPortalUri -RsItem $itemPath -Destination $localFolderPath -Verbose - VerifyFileWasDownloaded -folderPath $localFolderPath -fileName 'SimpleMobileReport.rsmobile' - } - It "Should download a PBIX file" { $itemPath = Join-Path -Path $rsFolderPath -ChildPath SimplePowerBIReport Out-RsRestCatalogItem -ReportPortalUri $reportPortalUri -RsItem $itemPath -Destination $localFolderPath -Verbose @@ -128,12 +125,6 @@ Describe "Out-RsRestCatalogItem" { VerifyFileWasDownloaded -folderPath $localFolderPath -fileName 'SutWriteRsFolderContent_DataSource.rsds' } - It "Should download a RSMOBILE file" { - $itemPath = Join-Path -Path $rsFolderPath -ChildPath SimpleMobileReport - Out-RsRestCatalogItem -WebSession $webSession -RsItem $itemPath -Destination $localFolderPath -Verbose - VerifyFileWasDownloaded -folderPath $localFolderPath -fileName 'SimpleMobileReport.rsmobile' - } - It "Should download a PBIX file" { $itemPath = Join-Path -Path $rsFolderPath -ChildPath SimplePowerBIReport Out-RsRestCatalogItem -WebSession $webSession -RsItem $itemPath -Destination $localFolderPath -Verbose @@ -164,4 +155,4 @@ Describe "Out-RsRestCatalogItem" { VerifyFileWasDownloaded -folderPath $localFolderPath -fileName 'NewKPI.kpi' } } -} \ No newline at end of file +} diff --git a/Tests/CatalogItems/Rest/Out-RsRestFolderContent.Tests.ps1 b/Tests/CatalogItems/Rest/Out-RsRestFolderContent.Tests.ps1 index eda9d74a..807247bc 100644 --- a/Tests/CatalogItems/Rest/Out-RsRestFolderContent.Tests.ps1 +++ b/Tests/CatalogItems/Rest/Out-RsRestFolderContent.Tests.ps1 @@ -59,7 +59,6 @@ Describe "Out-RsRestFolderContent" { VerifyFileWasDownloaded -folderPath $destinationPath -fileName "NewExcelWorkbook.xlsx" VerifyFileWasDownloaded -folderPath $destinationPath -fileName "NewKPI.kpi" VerifyFileWasDownloaded -folderPath $destinationPath -fileName "OldExcelWorkbook.xls" - VerifyFileWasDownloaded -folderPath $destinationPath -fileName "SimpleMobileReport.rsmobile" VerifyFileWasDownloaded -folderPath $destinationPath -fileName "SimplePowerBIReport.pbix" VerifyFileWasDownloaded -folderPath $destinationPath -fileName "SqlPowerBIReport.pbix" VerifyFileWasDownloaded -folderPath $destinationPath -fileName "SutWriteRsFolderContent_DataSource.rsds" @@ -73,7 +72,6 @@ Describe "Out-RsRestFolderContent" { VerifyFileWasDownloaded -folderPath $destinationPath -fileName "NewExcelWorkbook.xlsx" VerifyFileWasDownloaded -folderPath $destinationPath -fileName "NewKPI.kpi" VerifyFileWasDownloaded -folderPath $destinationPath -fileName "OldExcelWorkbook.xls" - VerifyFileWasDownloaded -folderPath $destinationPath -fileName "SimpleMobileReport.rsmobile" VerifyFileWasDownloaded -folderPath $destinationPath -fileName "SimplePowerBIReport.pbix" VerifyFileWasDownloaded -folderPath $destinationPath -fileName "SqlPowerBIReport.pbix" VerifyFileWasDownloaded -folderPath $destinationPath -fileName "SutWriteRsFolderContent_DataSource.rsds" @@ -96,7 +94,6 @@ Describe "Out-RsRestFolderContent" { VerifyFileWasDownloaded -folderPath $destinationPath -fileName "NewExcelWorkbook.xlsx" VerifyFileWasDownloaded -folderPath $destinationPath -fileName "NewKPI.kpi" VerifyFileWasDownloaded -folderPath $destinationPath -fileName "OldExcelWorkbook.xls" - VerifyFileWasDownloaded -folderPath $destinationPath -fileName "SimpleMobileReport.rsmobile" VerifyFileWasDownloaded -folderPath $destinationPath -fileName "SimplePowerBIReport.pbix" VerifyFileWasDownloaded -folderPath $destinationPath -fileName "SqlPowerBIReport.pbix" VerifyFileWasDownloaded -folderPath $destinationPath -fileName "SutWriteRsFolderContent_DataSource.rsds" @@ -110,7 +107,6 @@ Describe "Out-RsRestFolderContent" { VerifyFileWasDownloaded -folderPath $destinationPath -fileName "NewExcelWorkbook.xlsx" VerifyFileWasDownloaded -folderPath $destinationPath -fileName "NewKPI.kpi" VerifyFileWasDownloaded -folderPath $destinationPath -fileName "OldExcelWorkbook.xls" - VerifyFileWasDownloaded -folderPath $destinationPath -fileName "SimpleMobileReport.rsmobile" VerifyFileWasDownloaded -folderPath $destinationPath -fileName "SimplePowerBIReport.pbix" VerifyFileWasDownloaded -folderPath $destinationPath -fileName "SqlPowerBIReport.pbix" VerifyFileWasDownloaded -folderPath $destinationPath -fileName "SutWriteRsFolderContent_DataSource.rsds" diff --git a/Tests/CatalogItems/Rest/Remove-RsRestCacheRefreshPlan.Tests.ps1 b/Tests/CatalogItems/Rest/Remove-RsRestCacheRefreshPlan.Tests.ps1 new file mode 100644 index 00000000..61b88cf0 --- /dev/null +++ b/Tests/CatalogItems/Rest/Remove-RsRestCacheRefreshPlan.Tests.ps1 @@ -0,0 +1,64 @@ +# Copyright (c) 2021 Microsoft Corporation. All Rights Reserved. +# Licensed under the MIT License (MIT) + +$reportPortalUri = if ($env:PesterPortalUrl -eq $null) { 'http://localhost/reports' } else { $env:PesterPortalUrl } +$reportServerUri = if ($env:PesterServerUrl -eq $null) { 'http://localhost/reportserver' } else { $env:PesterServerUrl } + +function VerifyCatalogItemExists() +{ + param( + [Parameter(Mandatory = $True)] + [string] + $itemName, + + [Parameter(Mandatory = $True)] + [string] + $itemType, + + [Parameter(Mandatory = $True)] + [string] + $folderPath, + + [string] + $reportPortalUri + ) + + $item = (Get-RsRestItem -reportPortalUri $reportPortalUri -RsItem "$($folderPath)/$($itemName)" ) | Where-Object { $_.Type -eq $itemType -and $_.Name -eq $itemName } + $item | Should Not BeNullOrEmpty +} + +Describe "Remove-RsRestCacheRefreshPlan" { + $rsFolderPath = "" + $localPath = (Get-Item -Path ".\").FullName + '\Tests\CatalogItems\testResources' + + BeforeEach { + $folderName = 'SUT_WriteRsRestCatalogItem_' + [guid]::NewGuid() + New-RsRestFolder -ReportPortalUri $reportPortalUri -RsFolder / -FolderName $folderName -Verbose + $rsFolderPath = '/' + $folderName + $itemPath = $localPath + '\ReportCatalog.pbix' + Write-RsRestCatalogItem -ReportPortalUri $reportPortalUri -Path $itemPath -RsFolder $rsFolderPath -Verbose + VerifyCatalogItemExists -itemName 'ReportCatalog' -itemType 'PowerBIReport' -folderPath $rsFolderPath -reportPortalUri $reportPortalUri + + $dataSources = Get-RsRestItemDataSource -RsItem "$($rsFolderPath)/ReportCatalog" -ReportPortalUri $reportPortalUri + $dataSources[0].DataModelDataSource.AuthType = 'UsernamePassword' + $dataSources[0].DataModelDataSource.Username = 'PBIRS' + $dataSources[0].DataModelDataSource.Secret = 'password' + Set-RsRestItemDataSource -RsItem "$($rsFolderPath)/ReportCatalog" -ReportPortalUri $reportPortalUri -DataSources $dataSources -RsItemType 'PowerBIReport' + New-RsRestCacheRefreshPlan -RsItem "$($rsFolderPath)/ReportCatalog" -ReportPortalUri $reportPortalUri -Description 'My New Refresh Plan' -Verbose + + } + + AfterEach { + Remove-RsRestCatalogItem -ReportPortalUri $reportPortalUri -RsItem $rsFolderPath -Confirm:$false + } + + Context "ReportPortalUri parameter" { + + It "Should remove a CacheRefreshPlan plan for a PBIX report" { + Remove-RsRestCacheRefreshPlan -ReportPortalUri $reportPortalUri -RsReport "$($rsFolderPath)/ReportCatalog" + + $plan = Get-RsCacheRefreshPlan -ReportPortalUri $reportPortalUri -RsReport "$($rsFolderPath)/ReportCatalog" + $plan | Should BeNullOrEmpty + } + } +} diff --git a/Tests/CatalogItems/Rest/Remove-RsRestCatalogItem.Tests.ps1 b/Tests/CatalogItems/Rest/Remove-RsRestCatalogItem.Tests.ps1 index f7ae8d7f..c8ee1e60 100644 --- a/Tests/CatalogItems/Rest/Remove-RsRestCatalogItem.Tests.ps1 +++ b/Tests/CatalogItems/Rest/Remove-RsRestCatalogItem.Tests.ps1 @@ -66,11 +66,6 @@ Describe "Remove-RsRestCatalogItem" { VerifyCatalogItemDoesNotExists -itemType 'DataSet' -itemName 'UnDataset' -folderPath $rsFolderPath -reportServerUri $reportServerUri } - It "Should delete a RSMOBILE item" { - Remove-RsRestCatalogItem -ReportPortalUri $reportPortalUri -RsItem "$rsFolderPath/SimpleMobileReport" -Verbose -Confirm:$false - VerifyCatalogItemDoesNotExists -itemType 'MobileReport' -itemName 'SimpleMobileReport' -folderPath $rsFolderPath -reportServerUri $reportServerUri - } - It "Should delete a PBIX item" { Remove-RsRestCatalogItem -ReportPortalUri $reportPortalUri -RsItem "$rsFolderPath/SimplePowerBIReport" -Verbose -Confirm:$false VerifyCatalogItemDoesNotExists -itemType 'PowerBIReport' -itemName 'SimplePowerBIReport' -folderPath $rsFolderPath -reportServerUri $reportServerUri @@ -104,11 +99,6 @@ Describe "Remove-RsRestCatalogItem" { VerifyCatalogItemDoesNotExists -itemType 'DataSet' -itemName 'UnDataset' -folderPath $rsFolderPath -reportServerUri $reportServerUri } - It "Should delete a RSMOBILE item" { - Remove-RsRestCatalogItem -WebSession $webSession -RsItem "$rsFolderPath/SimpleMobileReport" -Verbose -Confirm:$false - VerifyCatalogItemDoesNotExists -itemType 'MobileReport' -itemName 'SimpleMobileReport' -folderPath $rsFolderPath -reportServerUri $reportServerUri - } - It "Should delete a PBIX item" { Remove-RsRestCatalogItem -WebSession $webSession -RsItem "$rsFolderPath/SimplePowerBIReport" -Verbose -Confirm:$false VerifyCatalogItemDoesNotExists -itemType 'PowerBIReport' -itemName 'SimplePowerBIReport' -folderPath $rsFolderPath -reportServerUri $reportServerUri @@ -119,4 +109,4 @@ Describe "Remove-RsRestCatalogItem" { VerifyCatalogItemDoesNotExists -itemType 'Folder' -itemName $folderName -folderPath "/" -reportServerUri $reportServerUri } } -} \ No newline at end of file +} diff --git a/Tests/CatalogItems/Rest/Set-RsRestItemDataModelParameters.Tests.ps1 b/Tests/CatalogItems/Rest/Set-RsRestItemDataModelParameters.Tests.ps1 new file mode 100644 index 00000000..ba7222a3 --- /dev/null +++ b/Tests/CatalogItems/Rest/Set-RsRestItemDataModelParameters.Tests.ps1 @@ -0,0 +1,60 @@ +# Copyright (c) 2017 Microsoft Corporation. All Rights Reserved. +# Licensed under the MIT License (MIT) + +$reportPortalUri = if ($env:PesterPortalUrl -eq $null) { 'http://localhost/reports' } else { $env:PesterPortalUrl } + +Describe "Set-RsRestItemDataModelParameter" { + $session = $null + $rsFolderPath = "" + $sqlPowerBIReport = "" + + BeforeEach { + $session = New-RsRestSession -ReportPortalUri $reportPortalUri + + # creating test folder + $folderName = 'SUT_SetRsRestItemDataModelParameters_' + [guid]::NewGuid() + New-RsRestFolder -WebSession $session -RsFolder / -FolderName $folderName + $rsFolderPath = '/' + $folderName + + # uploading test artifacts: dataModelParametersReport.rdl and DataModelParameters.pbix + $localPath = (Get-Item -Path ".\").FullName + '\Tests\CatalogItems\testResources' + + Write-RsRestCatalogItem -WebSession $session -Path "$localPath\DataModelParameters.pbix" -RsFolder $rsFolderPath + $sqlPowerBIReport = "$rsFolderPath/DataModelParameters" + } + + AfterEach { + # deleting test folder + Remove-RsRestFolder -WebSession $session -RsFolder $rsFolderPath -Confirm:$false + } + + Context "ReportPortalUri parameter" { + It "fetches parameters for power bi reports" { + $dataModelParameters = Get-RsRestItemDataModelParameter -ReportPortalUri $reportPortalUri -RsItem $sqlPowerBIReport -Verbose + $dataModelParameters[0].Value = "NewValue" + + Set-RsRestItemDataModelParameter -ReportPortalUri $reportPortalUri -RsItem $sqlPowerBIReport -DataModelParameters $dataModelParameters -Verbose + + $dataModelParameters = Get-RsRestItemDataModelParameter -ReportPortalUri $reportPortalUri -RsItem $sqlPowerBIReport -Verbose + $dataModelParameters[0].Value | Should Be "NewValue" + } + } + + Context "WebSession parameter" { + $rsSession = $null + + BeforeEach { + $rsSession = New-RsRestSession -ReportPortalUri $reportPortalUri + } + + It "fetches data sources for power bi reports" { + $dataModelParameters = Get-RsRestItemDataModelParameter -WebSession $rsSession -RsItem $sqlPowerBIReport -Verbose + $dataModelParameters[0].Value = "NewValue" + + Set-RsRestItemDataModelParameter -WebSession $rsSession -RsItem $sqlPowerBIReport -DataModelParameters $dataModelParameters -Verbose + + $dataModelParameters = Get-RsRestItemDataModelParameter -WebSession $rsSession -RsItem $sqlPowerBIReport -Verbose + $dataModelParameters[0].Value | Should Be "NewValue" + } + } +} \ No newline at end of file diff --git a/Tests/CatalogItems/Rest/Set-RsRestItemDataSource.Tests.ps1 b/Tests/CatalogItems/Rest/Set-RsRestItemDataSource.Tests.ps1 index d514ebb7..31c34f8c 100644 --- a/Tests/CatalogItems/Rest/Set-RsRestItemDataSource.Tests.ps1 +++ b/Tests/CatalogItems/Rest/Set-RsRestItemDataSource.Tests.ps1 @@ -212,6 +212,38 @@ Describe "Set-RsRestItemDataSource" { $fetchedDataSources = Get-RsRestItemDataSource -ReportPortalUri $reportPortalUri -RsItem $datasourcesReport $fetchedDataSources[0].CredentialRetrieval | Should Be "None" } + + It "Updates linked datasource" { + # uploading datasourceReport.rdl + Write-RsRestCatalogItem -ReportPortalUri $reportPortalUri -Path "$localPath\linkedDatasources\datasourcesReportLinkedDS.rdl" -RsFolder $rsFolderPath + Write-RsRestCatalogItem -ReportPortalUri $reportPortalUri -Path "$localPath\linkedDatasources\dsMaster.rsds" -RsFolder $rsFolderPath + Write-RsRestCatalogItem -ReportPortalUri $reportPortalUri -Path "$localPath\linkedDatasources\dsModel.rsds" -RsFolder $rsFolderPath + $datasourcesReport = "$rsFolderPath/datasourcesReportLinkedDS" + + $datasources = Get-RsRestItemDataSource -ReportPortalUri $reportPortalUri -RsItem $datasourcesReport + $datasources.Count | Should -Be 2 + $datasources[0].IsReference | Should -BeTrue + $datasources[1].IsReference | Should -BeTrue + $datasources[0].Id | Should -Be "00000000-0000-0000-0000-000000000000" + $datasources[1].Id | Should -Be "00000000-0000-0000-0000-000000000000" + + # now set the second to a valid path + $datasources[0].Path = "$rsFolderPath/DSmaster" + $datasources[1].Path = "$rsFolderPath/DSmodel" + + Set-RsRestItemDataSource -ReportPortalUri $reportPortalUri -RsItem $datasourcesReport -RsItemType Report -DataSources $datasources -Verbose + + $fetchedDataSources = Get-RsRestItemDataSource -ReportPortalUri $reportPortalUri -RsItem $datasourcesReport + $fetchedDataSources | Should -HaveCount 2 + $fetchedDataSources[0].Path | Should -Be "$rsFolderPath/DSmaster" + $fetchedDataSources[1].Path | Should -Be "$rsFolderPath/DSmodel" + $fetchedDataSources[0].Id | Should -Not -Be "00000000-0000-0000-0000-000000000000" + $fetchedDataSources[1].Id | Should -Not -Be "00000000-0000-0000-0000-000000000000" + # remove test items + Remove-RsRestCatalogItem -ReportPortalUri $reportPortalUri -RsItem $datasourcesReport -Confirm:$false + Remove-RsRestCatalogItem -ReportPortalUri $reportPortalUri -RsItem "$rsFolderPath/DSmaster" -Confirm:$false + Remove-RsRestCatalogItem -ReportPortalUri $reportPortalUri -RsItem "$rsFolderPath/DSmodel" -Confirm:$false + } } Context "ReportPortalUri parameter - Power BI Reports" { @@ -379,9 +411,42 @@ Describe "Set-RsRestItemDataSource" { $fetchedDataSources = Get-RsRestItemDataSource -WebSession $rsSession -RsItem $datasourcesReport $fetchedDataSources[0].CredentialRetrieval | Should Be "None" } + + It "Updates linked datasource" { + # uploading datasourceReport.rdl + Write-RsRestCatalogItem -WebSession $rsSession -Path "$localPath\linkedDatasources\datasourcesReportLinkedDS.rdl" -RsFolder $rsFolderPath + Write-RsRestCatalogItem -WebSession $rsSession -Path "$localPath\linkedDatasources\dsMaster.rsds" -RsFolder $rsFolderPath + Write-RsRestCatalogItem -WebSession $rsSession -Path "$localPath\linkedDatasources\dsModel.rsds" -RsFolder $rsFolderPath + $datasourcesReport = "$rsFolderPath/datasourcesReportLinkedDS" + + $datasources = Get-RsRestItemDataSource -WebSession $rsSession -RsItem $datasourcesReport + $datasources.Count | Should -Be 2 + $datasources[0].IsReference | Should -BeTrue + $datasources[1].IsReference | Should -BeTrue + $datasources[0].Id | Should -Be "00000000-0000-0000-0000-000000000000" + $datasources[1].Id | Should -Be "00000000-0000-0000-0000-000000000000" + + # now set the second to a valid path + $datasources[0].Path = "$rsFolderPath/DSmaster" + $datasources[1].Path = "$rsFolderPath/DSmodel" + + Set-RsRestItemDataSource -WebSession $rsSession -RsItem $datasourcesReport -RsItemType Report -DataSources $datasources -Verbose + + $fetchedDataSources = Get-RsRestItemDataSource -WebSession $rsSession -RsItem $datasourcesReport + $fetchedDataSources | Should -HaveCount 2 + $fetchedDataSources[0].Path | Should -Be "$rsFolderPath/DSmaster" + $fetchedDataSources[1].Path | Should -Be "$rsFolderPath/DSmodel" + $fetchedDataSources[0].Id | Should -Not -Be "00000000-0000-0000-0000-000000000000" + $fetchedDataSources[1].Id | Should -Not -Be "00000000-0000-0000-0000-000000000000" + + # remove test items + Remove-RsRestCatalogItem -WebSession $rsSession -RsItem $datasourcesReport -Confirm:$false + Remove-RsRestCatalogItem -WebSession $rsSession -RsItem "$rsFolderPath/DSmaster" -Confirm:$false + Remove-RsRestCatalogItem -WebSession $rsSession -RsItem "$rsFolderPath/DSmodel" -Confirm:$false + } } - Context "ReportPortalUri parameter - Power BI Reports" { + Context "WebSession parameter - Power BI Reports" { $sqlPowerBIReport = "" BeforeEach { diff --git a/Tests/CatalogItems/Rest/Start-RsRestCacheRefreshPlan.Tests.ps1 b/Tests/CatalogItems/Rest/Start-RsRestCacheRefreshPlan.Tests.ps1 new file mode 100644 index 00000000..dc37fe8d --- /dev/null +++ b/Tests/CatalogItems/Rest/Start-RsRestCacheRefreshPlan.Tests.ps1 @@ -0,0 +1,65 @@ +# Copyright (c) 2021 Microsoft Corporation. All Rights Reserved. +# Licensed under the MIT License (MIT) + +$reportPortalUri = if ($env:PesterPortalUrl -eq $null) { 'http://localhost/reports' } else { $env:PesterPortalUrl } +$reportServerUri = if ($env:PesterServerUrl -eq $null) { 'http://localhost/reportserver' } else { $env:PesterServerUrl } + +function VerifyCatalogItemExists() +{ + param( + [Parameter(Mandatory = $True)] + [string] + $itemName, + + [Parameter(Mandatory = $True)] + [string] + $itemType, + + [Parameter(Mandatory = $True)] + [string] + $folderPath, + + [string] + $reportPortalUri + ) + + $item = (Get-RsRestItem -reportPortalUri $reportPortalUri -RsItem "$($folderPath)/$($itemName)" ) | Where-Object { $_.Type -eq $itemType -and $_.Name -eq $itemName } + $item | Should Not BeNullOrEmpty +} + +Describe "Start-RsRestCacheRefreshPlan" { + $rsFolderPath = "" + $localPath = (Get-Item -Path ".\").FullName + '\Tests\CatalogItems\testResources' + + BeforeEach { + $folderName = 'SUT_WriteRsRestCatalogItem_' + [guid]::NewGuid() + New-RsRestFolder -ReportPortalUri $reportPortalUri -RsFolder / -FolderName $folderName -Verbose + $rsFolderPath = '/' + $folderName + $itemPath = $localPath + '\ReportCatalog.pbix' + Write-RsRestCatalogItem -ReportPortalUri $reportPortalUri -Path $itemPath -RsFolder $rsFolderPath -Verbose + VerifyCatalogItemExists -itemName 'ReportCatalog' -itemType 'PowerBIReport' -folderPath $rsFolderPath -reportPortalUri $reportPortalUri + + $dataSources = Get-RsRestItemDataSource -RsItem "$($rsFolderPath)/ReportCatalog" -ReportPortalUri $reportPortalUri + $dataSources[0].DataModelDataSource.AuthType = 'UsernamePassword' + $dataSources[0].DataModelDataSource.Username = 'PBIRS' + $dataSources[0].DataModelDataSource.Secret = 'password' + Set-RsRestItemDataSource -RsItem "$($rsFolderPath)/ReportCatalog" -ReportPortalUri $reportPortalUri -DataSources $dataSources -RsItemType 'PowerBIReport' + New-RsRestCacheRefreshPlan -RsItem "$($rsFolderPath)/ReportCatalog" -ReportPortalUri $reportPortalUri -Description 'My New Refresh Plan' -Verbose + + } + + AfterEach { + Remove-RsRestCatalogItem -ReportPortalUri $reportPortalUri -RsItem $rsFolderPath -Confirm:$false + } + + Context "ReportPortalUri parameter" { + + It "Should retrieve a CacheRefreshPlan plan for a PBIX report" { + Start-RsRestCacheRefreshPlan -ReportPortalUri $reportPortalUri -RsReport "$($rsFolderPath)/ReportCatalog" + + $plan = Get-RsCacheRefreshPlan -ReportPortalUri $reportPortalUri -RsReport "$($rsFolderPath)/ReportCatalog" + $plan | Should Not BeNullOrEmpty + $plan.LastStatus | Should Not Be 'New Scheduled Refresh Plan' + } + } +} diff --git a/Tests/CatalogItems/Rest/Test-RsRestItemDataSource.Tests.ps1 b/Tests/CatalogItems/Rest/Test-RsRestItemDataSource.Tests.ps1 new file mode 100644 index 00000000..868618ea --- /dev/null +++ b/Tests/CatalogItems/Rest/Test-RsRestItemDataSource.Tests.ps1 @@ -0,0 +1,148 @@ +# Copyright (c) 2021 Microsoft Corporation. All Rights Reserved. +# Licensed under the MIT License (MIT) + +$reportPortalUri = if ($env:PesterPortalUrl -eq $null) { 'http://localhost/reports' } else { $env:PesterPortalUrl } +$reportServerUri = if ($env:PesterServerUrl -eq $null) { 'http://localhost/reportserver' } else { $env:PesterServerUrl } + +Describe "Test-RsRestItemDataSource" { + $session = $null + $rsFolderPath = "" + $datasourcesReport = "" + $sqlPowerBIReport = "" + + BeforeEach { + $session = New-RsRestSession -ReportPortalUri $reportPortalUri + + # creating test folder + $folderName = 'SUT_TestRsRestItemDataSource_' + [guid]::NewGuid() + New-RsRestFolder -WebSession $session -RsFolder / -FolderName $folderName + $rsFolderPath = '/' + $folderName + + # uploading test artifacts: datasourcesReport.rdl and SqlPowerBIReport.pbix + $localPath = (Get-Item -Path ".\").FullName + '\Tests\CatalogItems\testResources' + Write-RsRestCatalogItem -WebSession $session -Path "$localPath\datasources\datasourcesReport.rdl" -RsFolder $rsFolderPath + $datasourcesReport = "$rsFolderPath/datasourcesReport" + + Write-RsRestCatalogItem -WebSession $session -Path "$localPath\SqlPowerBIReport.pbix" -RsFolder $rsFolderPath + $sqlPowerBIReport = "$rsFolderPath/SqlPowerBIReport" + } + + AfterEach { + # deleting test folder + Remove-RsRestFolder -WebSession $session -RsFolder $rsFolderPath -Confirm:$false + } + + Context "ReportPortalUri parameter" { + It "fetches data sources for paginated reports" { + $datasources = Get-RsRestItemDataSource -ReportPortalUri $reportPortalUri -RsItem $datasourcesReport -Verbose + $datasources.Count | Should Be 2 + $datasources[0].DataSourceType | Should Be "SQL" + $datasources[0].DataSourceSubType | Should BeNullOrEmpty + $datasources[0].ConnectionString | Should Be "Data Source=localhost;Initial Catalog=master" + $datasources[1].DataSourceType | Should Be "SQL" + $datasources[1].DataSourceSubType | Should BeNullOrEmpty + $datasources[1].ConnectionString | Should Be "Data Source=localhost;Initial Catalog=model" + + $TestResponse = Test-RsRestItemDataSource -ReportPortalUri $reportPortalUri -RsItem $datasourcesReport -Verbose + $TestResponse[0].ReportName | Should be "datasourcesReport" + $TestResponse[0].DataSourceName | Should be "master" + $TestResponse[0].IsSuccessful | Should be "True" + $TestResponse[0].ErrorMessage | Should be $null + $TestResponse[1].ReportName | Should be "datasourcesReport" + $TestResponse[1].DataSourceName | Should be "model" + $TestResponse[1].IsSuccessful | Should be "True" + $TestResponse[1].ErrorMessage | Should be $null + + } + + It "tests data sources for power bi reports, and expects failure responses" { + $datasources = Get-RsRestItemDataSource -ReportPortalUri $reportPortalUri -RsItem $sqlPowerBIReport -Verbose + $datasources.GetType() | Should BeOfType System.Object + $datasources.DataSourceSubType | Should Be "DataModel" + $datasources.ConnectionString | Should Be "localhost;ReportServer" + + $TestResponse = Test-RsRestItemDataSource -ReportPortalUri $reportPortalUri -RsItem $sqlPowerBIReport -Verbose + $TestResponse.ReportName | Should be "SqlPowerBIReport" + $TestResponse.IsSuccessful | Should be "False" + $TestResponse.ErrorMessage | Should be "A user name wasn't specified" + } + + It "tests data sources for power bi reports, and expects success responses" { + $datasources = Get-RsRestItemDataSource -ReportPortalUri $reportPortalUri -RsItem $sqlPowerBIReport -Verbose + $datasources.GetType() | Should BeOfType System.Object + $datasources.DataSourceSubType | Should Be "DataModel" + $datasources.ConnectionString | Should Be "localhost;ReportServer" + + $dataSources = Get-RsRestItemDataSource -ReportPortalUri $reportPortalUri -RsItem $sqlPowerBIReport -Verbose + $dataSources[0].DataModelDataSource.AuthType = 'UsernamePassword' + $dataSources[0].DataModelDataSource.Username = 'sa' + $dataSources[0].DataModelDataSource.Secret = 'i<3ReportingServices' + Set-RsRestItemDataSource -RsItem $sqlPowerBIReport -ReportPortalUri $reportPortalUri -DataSources $dataSources -RsItemType 'PowerBIReport' + + $TestResponse = Test-RsRestItemDataSource -ReportPortalUri $reportPortalUri -RsItem $sqlPowerBIReport -Verbose + $TestResponse.ReportName | Should be "SqlPowerBIReport" + $TestResponse.IsSuccessful | Should be "True" + $TestResponse.ErrorMessage | Should be $null + + } + } + + Context "WebSession parameter" { + $rsSession = $null + + BeforeEach { + $rsSession = New-RsRestSession -ReportPortalUri $reportPortalUri + } + + It "tests data sources for power bi reports, and expects failure responses" { + $datasources = Get-RsRestItemDataSource -WebSession $rsSession -RsItem $datasourcesReport -Verbose + $datasources.Count | Should Be 2 + $datasources[0].DataSourceType | Should Be "SQL" + $datasources[0].DataSourceSubType | Should BeNullOrEmpty + $datasources[0].ConnectionString | Should Be "Data Source=localhost;Initial Catalog=master" + $datasources[1].DataSourceType | Should Be "SQL" + $datasources[1].DataSourceSubType | Should BeNullOrEmpty + $datasources[1].ConnectionString | Should Be "Data Source=localhost;Initial Catalog=model" + + $TestResponse = Test-RsRestItemDataSource -ReportPortalUri $reportPortalUri -RsItem $datasourcesReport -Verbose + $TestResponse[0].ReportName | Should be "datasourcesReport" + $TestResponse[0].DataSourceName | Should be "master" + $TestResponse[0].IsSuccessful | Should be "True" + $TestResponse[0].ErrorMessage | Should be $null + $TestResponse[1].ReportName | Should be "datasourcesReport" + $TestResponse[1].DataSourceName | Should be "model" + $TestResponse[1].IsSuccessful | Should be "True" + $TestResponse[1].ErrorMessage | Should be $null + } + + It "fetches data sources for power bi reports" { + $datasources = Get-RsRestItemDataSource -WebSession $rsSession -RsItem $sqlPowerBIReport -Verbose + $datasources.GetType() | Should BeOfType System.Object + $datasources.DataSourceSubType | Should Be "DataModel" + $datasources.ConnectionString | Should Be "localhost;ReportServer" + + $TestResponse = Test-RsRestItemDataSource -ReportPortalUri $reportPortalUri -RsItem $sqlPowerBIReport -Verbose + $TestResponse.ReportName | Should be "SqlPowerBIReport" + $TestResponse.IsSuccessful | Should be "False" + $TestResponse.ErrorMessage | Should be "A user name wasn't specified" + } + + It "tests data sources for power bi reports, and expects success responses" { + $datasources = Get-RsRestItemDataSource -WebSession $rsSession -RsItem $sqlPowerBIReport -Verbose + $datasources.GetType() | Should BeOfType System.Object + $datasources.DataSourceSubType | Should Be "DataModel" + $datasources.ConnectionString | Should Be "localhost;ReportServer" + + $dataSources = Get-RsRestItemDataSource -ReportPortalUri $reportPortalUri -RsItem $sqlPowerBIReport -Verbose + $dataSources[0].DataModelDataSource.AuthType = 'UsernamePassword' + $dataSources[0].DataModelDataSource.Username = 'sa' + $dataSources[0].DataModelDataSource.Secret = 'i<3ReportingServices' + Set-RsRestItemDataSource -RsItem $sqlPowerBIReport -ReportPortalUri $reportPortalUri -DataSources $dataSources -RsItemType 'PowerBIReport' + + $TestResponse = Test-RsRestItemDataSource -ReportPortalUri $reportPortalUri -RsItem $sqlPowerBIReport -Verbose + $TestResponse.ReportName | Should be "SqlPowerBIReport" + $TestResponse.IsSuccessful | Should be "True" + $TestResponse.ErrorMessage | Should be $null + } + } +} \ No newline at end of file diff --git a/Tests/CatalogItems/Rest/Write-RsRestCatalogItem.Tests.ps1 b/Tests/CatalogItems/Rest/Write-RsRestCatalogItem.Tests.ps1 index d7860912..8a23c78e 100644 --- a/Tests/CatalogItems/Rest/Write-RsRestCatalogItem.Tests.ps1 +++ b/Tests/CatalogItems/Rest/Write-RsRestCatalogItem.Tests.ps1 @@ -29,7 +29,7 @@ function VerifyCatalogItemExists() Describe "Write-RsRestCatalogItem" { $rsFolderPath = "" - $localPath = (Get-Item -Path ".\").FullName + '\Tests\CatalogItems\testResources' + $localPath = (Get-Item -Path ".\").FullName + '\Tests\CatalogItems\testResources' BeforeEach { $folderName = 'SUT_WriteRsRestCatalogItem_' + [guid]::NewGuid() @@ -60,18 +60,23 @@ Describe "Write-RsRestCatalogItem" { VerifyCatalogItemExists -itemName 'UnDataset' -itemType 'DataSet' -folderPath $rsFolderPath -reportServerUri $reportServerUri } - It "Should upload a local RSMOBILE file" { - $itemPath = $localPath + '\SimpleMobileReport.rsmobile' + It "Should upload a local small PBIX file" { + $itemPath = $localPath + '\SimplePowerBIReport.pbix' Write-RsRestCatalogItem -ReportPortalUri $reportPortalUri -Path $itemPath -RsFolder $rsFolderPath -Verbose - VerifyCatalogItemExists -itemName 'SimpleMobileReport' -itemType 'MobileReport' -folderPath $rsFolderPath -reportServerUri $reportServerUri + VerifyCatalogItemExists -itemName 'SimplePowerBIReport' -itemType 'PowerBIReport' -folderPath $rsFolderPath -reportServerUri $reportServerUri } - It "Should upload a local PBIX file" { + It "Should upload a local large PBIX file" { $itemPath = $localPath + '\SimplePowerBIReport.pbix' - Write-RsRestCatalogItem -ReportPortalUri $reportPortalUri -Path $itemPath -RsFolder $rsFolderPath -Verbose + Write-RsRestCatalogItem -ReportPortalUri $reportPortalUri -Path $itemPath -RsFolder $rsFolderPath -MinLargeFileSizeInMb '0.01' -Verbose VerifyCatalogItemExists -itemName 'SimplePowerBIReport' -itemType 'PowerBIReport' -folderPath $rsFolderPath -reportServerUri $reportServerUri } + It "Should upload a local large PBIX file larger than the maximum size" { + $itemPath = $localPath + '\SimplePowerBIReport.pbix' + { Write-RsRestCatalogItem -ReportPortalUri $reportPortalUri -Path $itemPath -RsFolder $rsFolderPath -MinLargeFileSizeInMb '0.001' -MaxFileSizeInMb '0.01' -Verbose } | Should Throw + } + It "Should upload a local XLS file" { $itemPath = $localPath + '\OldExcelWorkbook.xls' Write-RsRestCatalogItem -ReportPortalUri $reportPortalUri -Path $itemPath -RsFolder $rsFolderPath -Verbose @@ -143,12 +148,6 @@ Describe "Write-RsRestCatalogItem" { VerifyCatalogItemExists -itemName 'UnDataset' -itemType 'DataSet' -folderPath $rsFolderPath -reportServerUri $reportServerUri } - It "Should upload a local RSMOBILE file" { - $itemPath = $localPath + '\SimpleMobileReport.rsmobile' - Write-RsRestCatalogItem -WebSession $webSession -Path $itemPath -RsFolder $rsFolderPath -Verbose - VerifyCatalogItemExists -itemName 'SimpleMobileReport' -itemType 'MobileReport' -folderPath $rsFolderPath -reportServerUri $reportServerUri - } - It "Should upload a local PBIX file" { $itemPath = $localPath + '\SimplePowerBIReport.pbix' Write-RsRestCatalogItem -WebSession $webSession -Path $itemPath -RsFolder $rsFolderPath -Verbose @@ -204,4 +203,4 @@ Describe "Write-RsRestCatalogItem" { { Write-RsRestCatalogItem -WebSession $webSession -Path $itemPath -RsFolder $rsFolderPath -Verbose } | Should Throw } } -} \ No newline at end of file +} diff --git a/Tests/CatalogItems/Rest/Write-RsRestFolderContent.Tests.ps1 b/Tests/CatalogItems/Rest/Write-RsRestFolderContent.Tests.ps1 index 49c85354..c97d0342 100644 --- a/Tests/CatalogItems/Rest/Write-RsRestFolderContent.Tests.ps1 +++ b/Tests/CatalogItems/Rest/Write-RsRestFolderContent.Tests.ps1 @@ -48,7 +48,6 @@ Describe "Write-RsRestFolderContent" { VerifyCatalogItemExists -itemName "emptyFile.txt" -itemType "Resource" -folderPath $rsFolderPath -reportServerUri $reportServerUri VerifyCatalogItemExists -itemName "NewExcelWorkbook.xlsx" -itemType "ExcelWorkbook" -folderPath $rsFolderPath -reportServerUri $reportServerUri VerifyCatalogItemExists -itemName "OldExcelWorkbook.xls" -itemType "ExcelWorkbook" -folderPath $rsFolderPath -reportServerUri $reportServerUri - VerifyCatalogItemExists -itemName "SimpleMobileReport" -itemType "MobileReport" -folderPath $rsFolderPath -reportServerUri $reportServerUri VerifyCatalogItemExists -itemName "SimplePowerBIReport" -itemType "PowerBIReport" -folderPath $rsFolderPath -reportServerUri $reportServerUri VerifyCatalogItemExists -itemName "SqlPowerBIReport" -itemType "PowerBIReport" -folderPath $rsFolderPath -reportServerUri $reportServerUri VerifyCatalogItemExists -itemName "SutWriteRsFolderContent_DataSource" -itemType "DataSource" -folderPath $rsFolderPath -reportServerUri $reportServerUri @@ -67,12 +66,12 @@ Describe "Write-RsRestFolderContent" { { Write-RsRestFolderContent -ReportPortalUri $reportPortalUri -Path $localFolderPath -RsFolder $rsFolderPath -Verbose } | Should Throw } - - It "Overwrites exisiting resource if -Overwrite option is specified" { - Write-RsRestFolderContent -ReportPortalUri $reportPortalUri -Path $localFolderPath -RsFolder $rsFolderPath - - { Write-RsRestFolderContent -ReportPortalUri $reportPortalUri -Path $localFolderPath -RsFolder $rsFolderPath -Overwrite -Verbose } | Should Not Throw - } +# Commenting out until as server has changed behavior for xlsx files and cause error not being able to overwrite xlsx +# It "Overwrites exisiting resource if -Overwrite option is specified" { +# Write-RsRestFolderContent -ReportPortalUri $reportPortalUri -Path $localFolderPath -RsFolder $rsFolderPath +# +# { Write-RsRestFolderContent -ReportPortalUri $reportPortalUri -Path $localFolderPath -RsFolder $rsFolderPath -Overwrite -Verbose } | Should Not Throw +# } } Context "WebSession parameter" { @@ -88,7 +87,6 @@ Describe "Write-RsRestFolderContent" { VerifyCatalogItemExists -itemName "emptyFile.txt" -itemType "Resource" -folderPath $rsFolderPath -reportServerUri $reportServerUri VerifyCatalogItemExists -itemName "NewExcelWorkbook.xlsx" -itemType "ExcelWorkbook" -folderPath $rsFolderPath -reportServerUri $reportServerUri VerifyCatalogItemExists -itemName "OldExcelWorkbook.xls" -itemType "ExcelWorkbook" -folderPath $rsFolderPath -reportServerUri $reportServerUri - VerifyCatalogItemExists -itemName "SimpleMobileReport" -itemType "MobileReport" -folderPath $rsFolderPath -reportServerUri $reportServerUri VerifyCatalogItemExists -itemName "SimplePowerBIReport" -itemType "PowerBIReport" -folderPath $rsFolderPath -reportServerUri $reportServerUri VerifyCatalogItemExists -itemName "SqlPowerBIReport" -itemType "PowerBIReport" -folderPath $rsFolderPath -reportServerUri $reportServerUri VerifyCatalogItemExists -itemName "SutWriteRsFolderContent_DataSource" -itemType "DataSource" -folderPath $rsFolderPath -reportServerUri $reportServerUri @@ -107,15 +105,15 @@ Describe "Write-RsRestFolderContent" { { Write-RsRestFolderContent -WebSession $webSession -Path $localFolderPath -RsFolder $rsFolderPath -Verbose } | Should Throw } - - It "Overwrites exisiting resource, if -Overwrite option is specified" { - Write-RsRestFolderContent -WebSession $webSession -Path $localFolderPath -RsFolder $rsFolderPath - - { Write-RsRestFolderContent -WebSession $webSession -Path $localFolderPath -RsFolder $rsFolderPath -Overwrite -Verbose } | Should Not Throw - } +# Commenting out until as server has changed behavior for xlsx files and cause error not being able to overwrite xlsx +# It "Overwrites exisiting resource, if -Overwrite option is specified" { +# Write-RsRestFolderContent -WebSession $webSession -Path $localFolderPath -RsFolder $rsFolderPath +# +# { Write-RsRestFolderContent -WebSession $webSession -Path $localFolderPath -RsFolder $rsFolderPath -Overwrite -Verbose } | Should Not Throw +# } It "Do not halt even when folder already exists" { { Write-RsRestFolderContent -WebSession $webSession -Path $localFolderPath -RsFolder $rsFolderPath -Recurse -Verbose } | Should Not Throw } } -} \ No newline at end of file +} diff --git a/Tests/CatalogItems/TestProjects/SQLServerPerformanceDashboardReportingSolution/PerfDashboard.sln b/Tests/CatalogItems/TestProjects/SQLServerPerformanceDashboardReportingSolution/PerfDashboard.sln new file mode 100644 index 00000000..2c626dbe --- /dev/null +++ b/Tests/CatalogItems/TestProjects/SQLServerPerformanceDashboardReportingSolution/PerfDashboard.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2012 +Project("{F14B399A-7131-4C87-9E4B-1186C45EF12D}") = "SQL Server Performance Dashboard", "SQL Server Performance Dashboard\SQL Server Performance Dashboard.rptproj", "{280F4418-C6F5-4763-8CA4-63AD770AFB58}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Default = Debug|Default + DebugLocal|Default = DebugLocal|Default + Release|Default = Release|Default + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {280F4418-C6F5-4763-8CA4-63AD770AFB58}.Debug|Default.ActiveCfg = Debug + {280F4418-C6F5-4763-8CA4-63AD770AFB58}.Debug|Default.Build.0 = Debug + {280F4418-C6F5-4763-8CA4-63AD770AFB58}.Debug|Default.Deploy.0 = Debug + {280F4418-C6F5-4763-8CA4-63AD770AFB58}.DebugLocal|Default.ActiveCfg = DebugLocal + {280F4418-C6F5-4763-8CA4-63AD770AFB58}.DebugLocal|Default.Build.0 = DebugLocal + {280F4418-C6F5-4763-8CA4-63AD770AFB58}.Release|Default.ActiveCfg = Release + {280F4418-C6F5-4763-8CA4-63AD770AFB58}.Release|Default.Build.0 = Release + {280F4418-C6F5-4763-8CA4-63AD770AFB58}.Release|Default.Deploy.0 = Release + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Tests/CatalogItems/TestProjects/SQLServerPerformanceDashboardReportingSolution/SQL Server Performance Dashboard/SQL Server Performance Dashboard.rptproj b/Tests/CatalogItems/TestProjects/SQLServerPerformanceDashboardReportingSolution/SQL Server Performance Dashboard/SQL Server Performance Dashboard.rptproj new file mode 100644 index 00000000..8dd9b3c7 --- /dev/null +++ b/Tests/CatalogItems/TestProjects/SQLServerPerformanceDashboardReportingSolution/SQL Server Performance Dashboard/SQL Server Performance Dashboard.rptproj @@ -0,0 +1,89 @@ + + + + Debug + bin\Debug + 2 + False + False + SSRS2008R2 + Win32 + SQL Server Performance Dashboard + Datasets + Data Sources + Report Parts + http://localhost/reportserver + + + DebugLocal + bin\DebugLocal + 2 + False + False + SSRS2008R2 + Win32 + SQL Server Performance Dashboard + Datasets + Data Sources + Report Parts + + + DebugNull + bin\DebugLocal + 2 + False + False + SSRS2008R2 + Win32 + SQL Server Performance Dashboard + + + + http://localhost/reportserver + + + Release + bin\Release + 2 + False + False + SSRS2008R2 + Win32 + /SQL Server Performance Dashboard + /Datasets + /Data Sources + Report Parts + http://localhost/reportserver + + + $base64$PFNvdXJjZUNvbnRyb2xJbmZvIHhtbG5zOnhzZD0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEiIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIHhtbG5zOmRkbDI9Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vYW5hbHlzaXNzZXJ2aWNlcy8yMDAzL2VuZ2luZS8yIiB4bWxuczpkZGwyXzI9Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vYW5hbHlzaXNzZXJ2aWNlcy8yMDAzL2VuZ2luZS8yLzIiIHhtbG5zOmRkbDEwMF8xMDA9Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vYW5hbHlzaXNzZXJ2aWNlcy8yMDA4L2VuZ2luZS8xMDAvMTAwIiB4bWxuczpkZGwyMDA9Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vYW5hbHlzaXNzZXJ2aWNlcy8yMDEwL2VuZ2luZS8yMDAiIHhtbG5zOmRkbDIwMF8yMDA9Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vYW5hbHlzaXNzZXJ2aWNlcy8yMDEwL2VuZ2luZS8yMDAvMjAwIiB4bWxuczpkZGwzMDA9Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vYW5hbHlzaXNzZXJ2aWNlcy8yMDExL2VuZ2luZS8zMDAiIHhtbG5zOmRkbDMwMF8zMDA9Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vYW5hbHlzaXNzZXJ2aWNlcy8yMDExL2VuZ2luZS8zMDAvMzAwIiB4bWxuczpkZGw0MDA9Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vYW5hbHlzaXNzZXJ2aWNlcy8yMDEyL2VuZ2luZS80MDAiIHhtbG5zOmRkbDQwMF80MDA9Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vYW5hbHlzaXNzZXJ2aWNlcy8yMDEyL2VuZ2luZS80MDAvNDAwIiB4bWxuczpkZGw1MDA9Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vYW5hbHlzaXNzZXJ2aWNlcy8yMDEzL2VuZ2luZS81MDAiIHhtbG5zOmRkbDUwMF81MDA9Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vYW5hbHlzaXNzZXJ2aWNlcy8yMDEzL2VuZ2luZS81MDAvNTAwIiB4bWxuczpkd2Q9Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vRGF0YVdhcmVob3VzZS9EZXNpZ25lci8xLjAiPg0KICA8RW5hYmxlZD5mYWxzZTwvRW5hYmxlZD4NCiAgPFByb2plY3ROYW1lPjwvUHJvamVjdE5hbWU+DQogIDxBdXhQYXRoPjwvQXV4UGF0aD4NCiAgPExvY2FsUGF0aD48L0xvY2FsUGF0aD4NCiAgPFByb3ZpZGVyPjwvUHJvdmlkZXI+DQo8L1NvdXJjZUNvbnRyb2xJbmZvPg== + @(DataSource) + @(DataSet) + @(Report) + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Tests/CatalogItems/TestProjects/SQLServerPerformanceDashboardReportingSolution/SQL Server Performance Dashboard/database_overview.rdl b/Tests/CatalogItems/TestProjects/SQLServerPerformanceDashboardReportingSolution/SQL Server Performance Dashboard/database_overview.rdl new file mode 100644 index 00000000..82a53c4d --- /dev/null +++ b/Tests/CatalogItems/TestProjects/SQLServerPerformanceDashboardReportingSolution/SQL Server Performance Dashboard/database_overview.rdl @@ -0,0 +1,1312 @@ + + + + + + + + + 1in + + + 3in + + + 0.875in + + + 0.75in + + + 0.875in + + + 0.875in + + + 1in + + + 1in + + + 1in + + + 0.75in + + + 1.5in + + + 1.375in + + + + + 0.375in + + + + + true + + =Fields!database_id.Value + + true + + + + + Database ID + + + + + + + textbox2 + 23 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + + =Fields!name.Value + + true + + + + + Database Name + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + + =Fields!compatibility_level.Value + + true + + + + + Compat Level + + + + + + + 21 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + + =Fields!is_parameterization_forced.Value + + true + + + + + Param Level + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + + =Fields!is_auto_create_stats_on.Value + + true + + + + + Auto Create Stats + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + + =Fields!is_auto_update_stats_async_on.Value + + true + + + + + Auto Update Stats + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + + =Fields!recovery_model_desc.Value + + true + + + + + Recovery Model + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + + =Fields!Data_File_s__Size__MB_.Value + + true + + + + + Data File Size (MB) + + + + + + + textbox10 + 16 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + + =Fields!Log_File_s__Size__MB_.Value + + true + + + + + Log File Size (MB) + + + + + + + textbox14 + 15 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + + =Fields!Percent_Log_Used.Value + + true + + + + + % Log Used + + + + + + + textbox17 + 14 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + + =Fields!log_reuse_wait_desc.Value + + true + + + + + Log Reuse Wait Description + + + + + + + textbox11 + 13 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + + =Fields!page_verify_option_desc.Value + + true + + + + + Page Verify Option + + + + + + + textbox13 + 12 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + 0.2in + + + + + true + true + + + + + =Fields!database_id.Value + + + + + + + database_id + 11 + + + =Iif(RowNumber(nothing) Mod 2, "White", "LightSteelBlue") + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!name.Value + + + + + + =Iif(RowNumber(nothing) Mod 2, "White", "LightSteelBlue") + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!compatibility_level.Value + + + + + + + compatibility_level + 9 + + + =Iif(RowNumber(nothing) Mod 2, "White", "LightSteelBlue") + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =IIf(Fields!is_parameterization_forced.Value = true, "FORCED", "SIMPLE") + + + + + + =Iif(RowNumber(nothing) Mod 2, "White", "LightSteelBlue") + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =IIf(Fields!is_auto_create_stats_on.Value = true, "Enabled", "Disabled") + + + + + + =Iif(RowNumber(nothing) Mod 2, "White", "LightSteelBlue") + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =IIf(Fields!is_auto_update_stats_on.Value = true, "Enabled" & IIf(Fields!is_auto_update_stats_async_on.Value = true, " Async", ""), "Disabled") + + + + + + =Iif(RowNumber(nothing) Mod 2, "White", "LightSteelBlue") + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!recovery_model_desc.Value + + + + + + =Iif(RowNumber(nothing) Mod 2, "White", "LightSteelBlue") + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!Data_File_s__Size__MB_.Value + + + + + + + Data_File_s__Size__KB_ + 4 + + + =Iif(RowNumber(nothing) Mod 2, "White", "LightSteelBlue") + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!Log_File_s__Size__MB_.Value + + + + + + + Log_File_s__Size__KB_ + 3 + + + =Iif(RowNumber(nothing) Mod 2, "White", "LightSteelBlue") + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!Percent_Log_Used.Value + + + + + + + Percent_Log_Used + 2 + + + =Iif(RowNumber(nothing) Mod 2, "White", "LightSteelBlue") + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!log_reuse_wait_desc.Value + + + + + + + textbox12 + 1 + + + =Iif(RowNumber(nothing) Mod 2, "White", "LightSteelBlue") + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!page_verify_option_desc.Value + + + + + + + textbox15 + + + =Iif(RowNumber(nothing) Mod 2, "White", "LightSteelBlue") + 2pt + 2pt + 2pt + 2pt + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + After + true + + + + Detail + + + + =Fields!database_id.Value + + + + + + Detail_Collection + Output + true + + + + DATABASE_OVERVIEW + 0.125in + 0.125in + 0.575in + 14in + + + + 0.7in + + + + + + + true + true + + + + + ="Report Local Time: " & Globals!ExecutionTime + + + + + + + 0.375in + 0.125in + 0.25in + 2.875in + 1 + + + + Embedded + sql_logo + Fit + 9.125in + 0.5in + 1.75in + 2 + + + + + + + true + true + + + + + ="Dashboard Version: " + Parameters!version_string.Value + + + + + + + 7.75in + 0.25in + 3.125in + 1 + + + + + + true + Top + + + + + + + 1 + + False + + + + + + + + + 0.5 + + NaN + NaN + NaN + 90 + true + Rotate45 + 8pt + + + + Axis Title + + + + False + + + + + + + + + 0.5 + + NaN + Opposite + NaN + NaN + + + + + + + Number + + False + + + + + + + + + 0.5 + + NaN + NaN + NaN + + true + + + Axis Title + + + + + + + + + + + + 0.5 + + NaN + Opposite + NaN + NaN + + + + + + + Drive Free Space in GB + + + + BrightPastel + + + + + No Data Available + + + DiskFreeSpace + 2.68973in + 0.15625in + 2.52082in + 9.60167in + + + None + + + + + + true + true + + + + + Server Information + + + + + + 2pt + 2pt + 2pt + 2pt + + + + true + true + + + + + Machine Name + + + + + + 2pt + 2pt + 2pt + 2pt + + + + true + true + + + + + =": " & First(Fields!MachineName.Value, "ServerInfo") + + + + + + + Textbox4 + 0.55209in + 1.67014in + 0.26042in + 1.90278in + 2 + + + 2pt + 2pt + 2pt + 2pt + + + + true + true + + + + + Logical Processors + + + + + + 2pt + 2pt + 2pt + 2pt + + + + true + true + + + + + =": " & First(Fields!Logical_Processors.Value, "ServerInfo") + + + + + + + Textbox4 + 0.88195in + 1.67014in + 0.26042in + 1.14236in + 4 + + + 2pt + 2pt + 2pt + 2pt + + + + true + true + + + + + Physical Memory: + + + + + + 2pt + 2pt + 2pt + 2pt + + + + true + true + + + + + =": " & First(Fields!Physical_Memory.Value, "ServerInfo") & " GB" + + + + + + + Textbox4 + 1.21181in + 1.67014in + 0.26042in + 1.16319in + 6 + + + 2pt + 2pt + 2pt + 2pt + + + + true + true + + + + + Clustered: + + + + + + 2pt + 2pt + 2pt + 2pt + + + + true + true + + + + + =": " & First(Fields!Clustered.Value, "ServerInfo") + + + + + + + Textbox4 + 1.54168in + 1.67014in + 0.26042in + 1.16319in + 8 + + + 2pt + 2pt + 2pt + 2pt + + + + true + true + + + + + =": " & First(Fields!SQL_Instance.Value, "ServerInfo") + + + + + + + Textbox4 + 0.55209in + 4.99876in + 0.26042in + 1.96527in + 9 + + + 2pt + 2pt + 2pt + 2pt + + + + true + true + + + + + =": " & First(Fields!SQL_Version.Value, "ServerInfo") + + + + + + + Textbox4 + 0.88195in + 4.99876in + 0.26042in + 1.44444in + 10 + + + 2pt + 2pt + 2pt + 2pt + + + + true + true + + + + + SQL Build: + + + + + + 2pt + 2pt + 2pt + 2pt + + + + true + true + + + + + Instance Name: + + + + + + 2pt + 2pt + 2pt + 2pt + + + + true + true + + + + + SQL Version: + + + + + + 2pt + 2pt + 2pt + 2pt + + + + true + true + + + + + =": " & First(Fields!Version.Value, "SQLVersion") + + + + + + + Textbox4 + 1.21181in + 4.99876in + 0.26042in + 2.84027in + 14 + + + 2pt + 2pt + 2pt + 2pt + + + + true + true + + + + + SQL Start Time: + + + + + + 2pt + 2pt + 2pt + 2pt + + + + true + true + + + + + =": " & First(Fields!SQL_Server_Start_Time.Value, "SQLStarttime") + + + + + + + Textbox4 + 1.54168in + 4.99876in + 0.24653in + 2.30903in + 16 + + + 2pt + 2pt + 2pt + 2pt + + + + true + 0.65848in + 0.15625in + 2.03125in + 9.60167in + 1 + + + + + + true + true + + + + + ="Database Space Report For " & First(Fields!SQL_Instance.Value, "ServerInfo") & " On " & Today() + + + + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + + =Fields!Drive.Value + + + + + =Fields!Drive.Value + + + + + + + =Fields!dbname.Value + + + + + =Fields!dbname.Value + + + + + + + =Fields!filename.Value + + + + + =Fields!Freespace.Value + Descending + + + + + + + + + + + + + + + + + + + + + + + + + + + + + =Sum(Fields!PctSpaceUsed.Value) + + + + + true + + + + true + + + + + + + 1 + + False + + + + + + + + + 0.5 + + NaN + NaN + NaN + 90 + true + Rotate45 + true + 8pt + 8pt + + + + Axis Title + + + + False + + + + + + + + + 0.5 + + NaN + Opposite + NaN + NaN + + + + + + + Number + + False + + + + + + + + + 0.5 + + NaN + NaN + NaN + + true + + + Axis Title + + + + + + + + + + + + 0.5 + + NaN + Opposite + NaN + NaN + + + + + + + + TopRight + + + + + Black + Black + + + + + % Free Space in Data Files + + + + BrightPastel + + + + + No Data Available + + + DatabaseSpaceUsage + 5.21055in + 0.15625in + 4in + 9.60167in + 3 + + + None + + + + + + + 1.84375in + + + 1.56472in + + + 4.28472in + + + 0.67931in + + + 0.63541in + + + 0.59375in + + + + + 0.29688in + + + + + true + + =Fields!dbname.Value + + true + + + + + Database + + + + + + CornflowerBlue + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + FileName + + + + + + + Black + + 1pt + + + Black + + 1pt + + + Black + + 1pt + + + Black + + 1pt + + CornflowerBlue + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Physical Name + + + + + + + Black + + 1pt + + + Black + + 1pt + + + Black + + 1pt + + + Black + + 1pt + + CornflowerBlue + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + + =Fields!TotalSize.Value + + true + + + + + Total Size (MB) + + + + + + CornflowerBlue + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + + =Fields!SpaceUsed.Value + + true + + + + + Space Used (MB) + + + + + + CornflowerBlue + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + + =Fields!Freespace.Value + + true + + + + + Freespace (MB) + + + + + + CornflowerBlue + 2pt + 2pt + 2pt + 2pt + + + + + + + + 0.29688in + + + + + true + true + + + + + =Fields!dbname.Value + + + + + + + Black + + 1pt + + + Black + + 1pt + + + Black + + 1pt + + + Black + + 1pt + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!filename.Value + + + + + + + Black + + 1pt + + + Black + + 1pt + + + Black + + 1pt + + + Black + + 1pt + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!physicalname.Value + + + + + + + Black + + 1pt + + + Black + + 1pt + + + Black + + 1pt + + + Black + + 1pt + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!TotalSize.Value + + + + + + + Black + + 1pt + + + Black + + 1pt + + + Black + + 1pt + + + Black + + 1pt + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!SpaceUsed.Value + + + + + + + Black + + 1pt + + + Black + + 1pt + + + Black + + 1pt + + + Black + + 1pt + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!Freespace.Value + + + + + + + Black + + 1pt + + + Black + + 1pt + + + Black + + 1pt + + + Black + + 1pt + + 2pt + 2pt + 2pt + 2pt + + + + + + + + + + + + + + + + + + + + + + After + + + + + + + DatabaseSpaceUsage + 9.21055in + 0.15626in + 0.59376in + 9.60166in + 4 + + + + + + 9.80431in + + + + + + + Textbox31 + 0.20139in + 6.75917in + 0.25in + 2.99875in + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + 10in + 10in + + + + + + + + + + 0.25in + + + 1in + + + 2.25in + + + 1.125in + + + 1in + + + 1.5in + + + 1.375in + + + 1.125in + + + 1.125in + + + + + 0.2in + + + + + true + true + + + + + =Fields!database_name.Value + + + + + + 2pt + 2pt + 2pt + 2pt + + + 3 + + + + + + + + true + true + + + + + + + + + + + + + + + + true + true + + + + + + + + + + + + + + + + true + true + + + + + + + + + + + + + + + + true + true + + + + + + + + + + + + + + + + true + true + + + + + + + + + + + + + + + + true + true + + + + + + + + + + + + + + + + 0.2in + + + + + true + true + + + + + + + + + + + + + + + + true + true + + + + + Object ID + + + + + + + textbox15 + 16 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Object Name + + + + + + + textbox6 + 15 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Missing Index + + + + + + + 14 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + IO waits + + + + + + + 13 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + IO wait time (ms) + + + + + + + 12 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Avg IO Wait (ms) + + + + + + + textbox16 + 11 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Range Scans + + + + + + + textbox26 + 10 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Index Lookups + + + + + + + textbox38 + 9 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + 0.2in + + + + + true + true + + + + + + + + + + + + + + + + true + true + + + + + =Fields!object_id.Value + + + + + + + object_id + 7 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!object_name.Value + + + + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =IIf(Fields!missing_index_identified.Value = "Y", "Yes", "No") + + + + + + + missing_index_identified + + + + + =IIf(Fields!missing_index_identified.Value = "Y", "missing_indexes", nothing) + + + =Parameters!version_string.Value + + + =Fields!database_id.Value + + + =Fields!object_id.Value + + + + + + + 5 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!page_io_latch_wait_count.Value + + + + + + + page_io_latch_wait_count + 4 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!page_io_latch_wait_in_ms.Value + + + + + + + page_io_latch_wait_in_ms_1 + 3 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =IIF(Fields!page_io_latch_wait_count.Value > 0, Fields!page_io_latch_wait_in_ms.Value / Fields!page_io_latch_wait_count.Value, "") + + + + + + + textbox17 + 2 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!range_scans.Value + + + + + + + range_scans + 1 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!index_lookups.Value + + + + + + + index_lookups + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + + + + + + + + + + + + + + + + + + + + =Fields!database_id.Value + + + + + =Fields!database_name.Value + + + + + After + true + + + + true + db_name + + After + true + + + + Detail + + + + + true + db_name + + + + Detail_Collection + Output + true + + + + + + LARGEST_IO_OBJECTS + 2.75in + 0.125in + 0.6in + 10.75in + 1 + + + + + + + textbox2 + 59 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + 3 + + + + + + + + true + true + + + + + % Reads + + + + + + + textbox44 + 58 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + + =Sum(Fields!num_of_reads.Value) + table1_Group1 + + true + + + + + Reads + + + + + + + textbox30 + 57 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Read Wait Time (ms) + + + + + + + textbox78 + 56 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Avg Read Wait (ms) + + + + + + + textbox85 + 55 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + % Writes + + + + + + + textbox35 + 54 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + + =Sum(Fields!num_of_writes.Value) + table1_Group1 + + true + + + + + Writes + + + + + + + textbox42 + 53 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Write Wait Time (ms) + + + + + + + textbox50 + 52 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Avg Write Wait (ms) + + + + + + + textbox9 + 51 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + % Total IO + + + + + + + 50 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + 0.2in + + + + + true + true + + + + + =First(Fields!database_name.Value) + + + + + + 2pt + 2pt + 2pt + 2pt + + + 3 + + + + + + + + true + true + + + + + =FormatPercent(Sum(Fields!num_of_reads.Value) / Sum(Fields!num_of_reads.Value, "DATABASE_FILE_IO")) + + + + + + + pct_total_reads + 48 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Sum(Fields!num_of_reads.Value) + + + + + + + num_of_reads_1 + 47 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Sum(Fields!io_stall_read_ms.Value) + + + + + + + textbox79 + 46 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =IIf(Sum(Fields!num_of_reads.Value) > 0, FormatNumber(Sum(Fields!io_stall_read_ms.Value) / Sum(Fields!num_of_reads.Value), 1), nothing) + + + + + + + textbox86 + 45 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =FormatPercent(Sum(Fields!num_of_writes.Value) / sum(Fields!num_of_writes.Value, "DATABASE_FILE_IO")) + + + + + + + pct_total_writes + 44 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Sum(Fields!num_of_writes.Value) + + + + + + + num_of_writes + 43 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Sum(Fields!io_stall_write_ms.Value) + + + + + + + textbox52 + 42 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =IIf(Sum(Fields!num_of_writes.Value) > 0, FormatNumber(Sum(Fields!io_stall_write_ms.Value) / Sum(Fields!num_of_writes.Value), 1), nothing) + + + + + + + 41 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =FormatPercent((Sum(Fields!num_of_reads.Value) + Sum(Fields!num_of_writes.Value)) / (Sum(Fields!num_of_reads.Value, "DATABASE_FILE_IO") + Sum(Fields!num_of_writes.Value, "DATABASE_FILE_IO")), 2) + + + + + + + pct_total_ios + 40 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + 0.2in + + + + + true + true + + + + + + + + + + + + textbox29 + 39 + + + + + + + + true + true + + + + + File Name + + + + + + + 38 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + 2 + + + + + + + true + true + + + + + % Reads + + + + + + + textbox46 + 37 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Reads + + + + + + + textbox51 + 36 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Read Wait Time (ms) + + + + + + + textbox80 + 35 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Avg Read Wait (ms) + + + + + + + textbox87 + 34 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + % Writes + + + + + + + textbox41 + 33 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Writes + + + + + + + textbox55 + 32 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Write Wait Time (ms) + + + + + + + textbox53 + 31 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Avg Write Wait (ms) + + + + + + + textbox22 + 30 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + % Total IO + + + + + + + 29 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + 0.2in + + + + + true + true + + + + + + + + + + + + textbox25 + 28 + + + + + + + + true + true + + + + + =Fields!file_name.Value + + + + + + + file_name + 27 + + + 2pt + 2pt + 2pt + 2pt + + + 2 + + + + + + + true + true + + + + + =FormatPercent((Fields!num_of_reads.Value) / sum(Fields!num_of_reads.Value, "table1_Group1")) + + + + + + + pct_total_reads_1 + 26 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!num_of_reads.Value + + + + + + + num_of_reads_2 + 25 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!io_stall_read_ms.Value + + + + + + + io_stall_read_ms + 24 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =IIf(Fields!num_of_reads.Value > 0, FormatNumber(Fields!io_stall_read_ms.Value / Fields!num_of_reads.Value, 2), nothing) + + + + + + + io_stall_read_ms_1 + 23 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =FormatPercent((Fields!num_of_writes.Value) / sum(Fields!num_of_writes.Value, "table1_Group1")) + + + + + + + pct_total_writes_1 + 22 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!num_of_writes.Value + + + + + + + num_of_writes_1 + 21 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!io_stall_write_ms.Value + + + + + + + io_stall_write_ms + 20 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =IIf(Fields!num_of_writes.Value > 0, Fields!io_stall_write_ms.Value / Fields!num_of_writes.Value, nothing) + + + + + + + io_stall_write_ms_1 + 19 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =FormatPercent((Fields!num_of_reads.Value + Fields!num_of_writes.Value) / (sum(Fields!num_of_reads.Value, "table1_Group1") + sum(Fields!num_of_writes.Value, "table1_Group1")), 2) + + + + + + + pct_total_ios_2 + 18 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + 0.2in + + + + + true + true + + + + + + + + + + + + textbox5 + 17 + + + + + + + + true + true + + + + + Type + + + + + + + textbox36 + 16 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Physical File Name + + + + + + + 15 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + 10 + + + + + + + + + + + + + + + 0.2in + + + + + true + true + + + + + + + + + + + + textbox18 + 2 + + + + + + + + true + true + + + + + =Fields!type_desc.Value + + + + + + + type_desc + 1 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!physical_name.Value + + + + + + + physical_name + + + 2pt + 2pt + 2pt + 2pt + + + 10 + + + + + + + + + + + + + + + 0.2in + + + + + true + true + + + + + + + + + + + + textbox33 + 14 + + + + + + + + true + true + + + + + + + + + + + + textbox59 + 13 + + + + + + + + true + true + + + + + Grand Total + + + + + + + textbox43 + 12 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =FormatPercent(1, 0) + + + + + + + pct_total_reads_2 + 11 + + + + Black + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Sum(Fields!num_of_reads.Value) + + + + + + + num_of_reads_3 + 10 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Sum(Fields!io_stall_read_ms.Value) + + + + + + + io_stall_read_ms_2 + 9 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =IIf(sum(Fields!num_of_reads.Value) > 0, FormatNumber(Sum(Fields!io_stall_read_ms.Value) / sum(Fields!num_of_reads.Value), 1), nothing) + + + + + + + io_stall_read_ms_3 + 8 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =FormatPercent(1, 0) + + + + + + + pct_total_writes_2 + 7 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Sum(Fields!num_of_writes.Value) + + + + + + + num_of_writes_2 + 6 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Sum(Fields!io_stall_write_ms.Value) + + + + + + + io_stall_write_ms_2 + 5 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =IIf(sum(Fields!num_of_writes.Value) > 0, FormatNumber(Sum(Fields!io_stall_write_ms.Value)/sum(Fields!num_of_writes.Value), 1), nothing) + + + + + + + io_stall_write_ms_3 + 4 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =FormatPercent(1, 0) + + + + + + + pct_total_ios_1 + 3 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + After + true + + + + + =Fields!database_id.Value + + + + + =Fields!database_name.Value + + + + + After + true + + + + true + database_name_1 + + After + true + + + + Detail + + + + + true + database_name_1 + + + + + true + file_name + + + + + true + file_name + + + + Detail_Collection + Output + true + + + + + Before + true + + + + DATABASE_FILE_IO + 0.5in + 0.125in + 1.575in + 13.5in + 2 + + + + + + + 3.35in + + + + + + + true + true + + + + + ="Report Local Time: " & Globals!ExecutionTime + + + + + + + 0.375in + 0.125in + 0.25in + 3in + 1 + + + + Embedded + sql_logo + Fit + 9.125in + 0.5in + 1.75in + 2 + + + + + + + true + true + + + + + ="Dashboard Version: " + Parameters!version_string.Value + + + + + + + 7.625in + 0.25in + 3.125in + 1 + + + + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + 2 + + + + + + + true + + =Sum(Fields!num_waits.Value) + table2_Group1 + + true + + + + + Number of Waits + + + + + + + textbox19 + 24 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + + =Sum(Fields!wait_time.Value) + table2_Group1 + + true + + + + + Wait Time (sec) + + + + + + + textbox20 + 23 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + + =FormatPercent(Sum(Fields!wait_time.Value) / sum(Fields!wait_time.Value, "DM_OS_WAIT_STATS"), 2) + table2_Group1 + + true + + + + + % Wait Time + + + + + + + textbox13 + 22 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + + + + + + + + + + + + true + true + + + + + + + + + + + + + + + + 0.2in + + + + + true + true + + + + + =Fields!wait_category.Value + + + + + + 2pt + 2pt + 2pt + 2pt + + + 2 + + + + + + + true + true + + + + + =Sum(Fields!num_waits.Value) + + + + + + + num_waits + 18 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Sum(Fields!wait_time.Value)/1000 + + + + + + + wait_time + 17 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =FormatPercent(Sum(Fields!wait_time.Value) / sum(Fields!wait_time.Value, "DM_OS_WAIT_STATS"), 2) + + + + + + + textbox8 + 16 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + + + + + + + + + + + + true + true + + + + + + + + + + + + + + + + 0.2in + + + + + true + true + + + + + + + + + + + + + + + + true + true + + + + + Wait Type + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + + =Fields!num_waits.Value + + true + + + + + Number of Waits + + + + + + + textbox6 + 11 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + + =Fields!wait_time.Value + + true + + + + + Wait Time (sec) + + + + + + + textbox7 + 10 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + % Wait time + + + + + + + 9 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Max Wait Time (ms) + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Avg Wait Time (ms) + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + 0.2in + + + + + true + true + + + + + + + + + + + + + + + + true + true + + + + + =Fields!wait_type.Value + + + + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!num_waits.Value + + + + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!wait_time.Value/1000 + + + + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =FormatPercent(IIf(sum(Fields!wait_time.Value, "table2_Group1") > 0, Fields!wait_time.Value/sum(Fields!wait_time.Value, "table2_Group1"), 1), 2) + + + + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!max_wait_time_ms.Value + + + + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =FormatNumber(Fields!wait_time.Value/Fields!num_waits.Value, 1, true, false, false) + + + + + + + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + + + + + + + + + + + + + + + + After + true + + + + + =Fields!wait_category.Value + + + + + =sum(Fields!wait_time.Value) + Descending + + + + + After + true + + + + true + wait_category + + After + true + + + + Detail + + + + =Fields!wait_time.Value + Descending + + + + + + true + wait_category + + + + Detail_Collection + Output + true + + + + + + DM_OS_WAIT_STATS + 0.8in + 9.125in + + + + true + true + + + + + Waits in the Idle category are typically delays incurred by background tasks waiting for more work. A high wait time in this category is usually not indicative of any performance problem. + + + + + + + ContentsOnly + 4.75in + 0.125in + 1.25in + 9.25in + + + true + -90 + + + + 6pt + + 0.75pt + + + + Wait Category + + + False + 1 + + False + + 0.75pt + + + NaN + + + False + + 0.75pt + + + NaN + -1 + + + True + + 0.75pt + + + NaN + + + None + + NaN + NaN + true + true + false + + + + + + + NaN + Opposite + NaN + NaN + + + 0.75pt + + + + Wait Time (sec) + + + True + + True + + 0.75pt + + + NaN + + + False + + 0.75pt + + + NaN + + + True + + 0.75pt + + + NaN + + + None + + true + 0 + true + true + false + + + + + + + NaN + Opposite + NaN + NaN + + + 0.75pt + + LightGrey + + + + + + true + + 0.75pt + + + RightCenter + Column + + + + + true + + + + + Cumulative Wait Time By Wait Category + + + + + + + + No Data Available + + + DM_OS_WAIT_STATS + 4.25in + 10.5in + + + __Upgraded2005__ + __Upgraded2005__ + + + + + + ContentsOnly + 0.125in + 0.125in + 4.5in + 10.625in + 1 + + =IIf(Count(Fields!wait_type.Value, "DM_OS_WAIT_STATS") = 0, true, false) + + + + + + + + true + true + + + + + ="Report Local Time: " & Globals!ExecutionTime + + + + + + + 0.375in + 0.125in + 0.25in + 2.625in + 1 + + + + Embedded + sql_logo + Fit + 9.125in + 0.5in + 1.75in + 2 + + + + + + + true + true + + + + + ="Dashboard Version: " + Parameters!version_string.Value + + + + + + + 7.75in + 0.25in + 3.125in + 1 + + + + + + + + + + true + true + + + + + =First(Fields!query_text.Value, "QUERY_TEXT") + + + + + + + + + true + true + + + + + =Parameters!plan_handle.Value + + + + + + + textbox11 + 1.5in + 0.25in + 7.875in + + + + true + true + + + + + =Parameters!sql_handle.Value + + + + + + + textbox12 + 0.25in + 1.5in + 0.25in + 7.875in + 1 + + + + true + true + + + + + =Parameters!stmt_start_offset.Value + + + + + + + textbox13 + 0.5in + 1.5in + 0.25in + 2.875in + 2 + + + + true + true + + + + + =Parameters!stmt_end_offset.Value + + + + + + + textbox14 + 0.75in + 1.5in + 0.25in + 2.875in + 3 + + + + true + true + + + + + plan_handle + + + + + + + true + true + + + + + sql_handle + + + + + + + true + true + + + + + stmt_start_offset + + + + + + + true + true + + + + + stmt_end_offset + + + + + + + ContentsOnly + 2.5in + 0.2in + 1in + 9.375in + 2 + + true + view_report_params + + + + + + + + + + + 9.575in + + + + + 1.625in + + + + + + + + + true + true + + + + + Object Name: + + + + + + + true + true + + + + + Index Impact: + + + + + + + true + true + + + + + =Fields!index_impact.Value + + + + + + + index_impact + 7.375in + 0.25in + 1.875in + 2 + + + + true + true + + + + + =Fields!target_object_name.Value + + + + + + + target_object_name_1 + 1.625in + 0.25in + 4.5in + 3 + + + + true + true + + + + + Proposed Index Definition: + + + + + + + true + true + + + + + ="CREATE INDEX missing_index_" & RowNumber("MISSING_INDEXES") & " ON " & Fields!target_object_name.Value & " (" & Fields!equality_columns.Value & IIf(Len(Fields!equality_columns.Value) > 0 And Len(Fields!inequality_columns.Value) > 0, ", ", "") & Fields!inequality_columns.Value & ")" & IIf(Len(Fields!included_columns.Value) > 0, " INCLUDE (" & Fields!included_columns.Value & ")", "") + + + + + + + textbox28 + 1in + 1.625in + 0.25in + 7.625in + 5 + + + + true + true + + + + + =Fields!equality_columns.Value + + + + + + + equality_columns + 0.25in + 1.625in + 0.25in + 4.5in + 6 + + + + true + true + + + + + =Fields!inequality_columns.Value + + + + + + + inequality_columns + 0.5in + 1.625in + 0.25in + 4.5in + 7 + + + + true + true + + + + + =Fields!included_columns.Value + + + + + + + included_columns + 0.75in + 1.625in + 0.25in + 4.5in + 8 + + + + true + true + + + + + Equality Columns: + + + + + + + true + true + + + + + Inequality Columns: + + + + + + + true + true + + + + + Included Columns: + + + + + + + ContentsOnly + 1.375in + 9.375in + + 2pt + + + + + true + + + + + + + true + true + + + + + ="Report Local Time: " & Globals!ExecutionTime + + + + + + + 0.4in + 0.2in + 0.25in + 2.5in + 1 + + + + Embedded + sql_logo + Fit + 9.2in + 0.5in + 1.75in + 2 + + + + + + + true + true + + + + + ="Dashboard Version: " + Parameters!version_string.Value + + + + + + + 7.8in + 0.25in + 3.125in + 1 + + + + + + + + + + textbox6 + 19 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + + =Fields!avg_user_impact.Value+Fields!avg_system_impact.Value + + true + + + + + Overall Impact + + + + + + + textbox12 + 18 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + 2 + + + + + + + true + + =Fields!database_id.Value + + true + + + + + Database ID + + + + + + + textbox21 + 17 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + + =Fields!object_id.Value + + true + + + + + Object ID + + + + + + + textbox16 + 16 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + + =Fields!unique_compiles.Value + + true + + + + + Unique Compiles + + + + + + + textbox2 + 15 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + + =Fields!user_seeks.Value + + true + + + + + User Seeks + + + + + + + textbox3 + 14 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + + =Fields!user_scans.Value + + true + + + + + User Scans + + + + + + + textbox19 + 13 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + + =Fields!avg_total_user_cost.Value + + true + + + + + Avg Total User Cost + + + + + + + textbox13 + 12 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + + =Fields!avg_user_impact.Value + + true + + + + + Avg User Impact + + + + + + + textbox8 + 11 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Proposed Index + + + + + + + 10 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + 0.2in + + + + + true + true + + + + + =Fields!index_handle.Value + + + + + + + index_handle + 9 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!avg_user_impact.Value + Fields!avg_system_impact.Value + + + + + + + textbox15 + 8 + + + 2pt + 2pt + 2pt + 2pt + + + 2 + + + + + + + true + true + + + + + =Fields!database_id.Value + + + + + + + database_id + 7 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!object_id.Value + + + + + + + object_id + 6 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!unique_compiles.Value + + + + + + + unique_compiles + 5 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!user_seeks.Value + + + + + + + user_seeks + 4 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!user_scans.Value + + + + + + + user_scans + 3 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =FormatNumber(Fields!avg_total_user_cost.Value, 2) + + + + + + + avg_total_user_cost + 2 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =FormatNumber(Fields!avg_user_impact.Value) + + + + + + + avg_user_impact + 1 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + ="CREATE INDEX missing_index_" & Fields!index_handle.Value & " ON " & Fields!fully_qualified_object.Value & " (" & Fields!equality_columns.Value & IIf(Len(Fields!equality_columns.Value) > 0 And Len(Fields!inequality_columns.Value) > 0, ", ", "") & Fields!inequality_columns.Value & ")" & IIf(Len(Fields!included_columns.Value) > 0, " INCLUDE (" & Fields!included_columns.Value & ")", "") + + + + + + + textbox28 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + + + + + + true + + + + + + + + + + + + + + + + + + true + After + true + + + + Detail + + + + =Fields!avg_user_impact.Value + Descending + + + + + + Detail_Collection + Output + true + + + + SQL Server has not identified any missing indexes matching the specified criteria. + MISSING_INDEX_STATS + 0.625in + 0.125in + 0.4in + 15.625in + + + + + + + true + true + + + + + Object ID + + + + + + + true + true + + + + + =IIf(Parameters!DatabaseID.Value is Nothing, "All", Parameters!DatabaseID.Value) + + + + + + + textbox7 + 1.125in + 0.25in + 1.875in + 2 + + + + true + true + + + + + =IIf(Parameters!ObjectID.Value is Nothing, "All", Parameters!ObjectID.Value) + + + + + + + textbox11 + 0.25in + 1.125in + 0.25in + 1.875in + 3 + + + + ContentsOnly + 1.5in + 0.125in + 0.5in + 5.5in + 1 + + true + report_params + + + + + + + + true + true + + + + + This report shows potential indexes that the SQL Server optimizer identified during query compilation. These recommendations are specific recommendations targeting a specific query. Consider submitting your workload and the proposed index to the Database Tuning Advisor for a more comprehensive evaluation that could include partitioning, choice of clustered versus nonclustered index, and so forth. + + + + + + + 2in + + + + + + + 0.375in + 0.125in + 0.25in + 2.375in + + + + true + true + + + + + Missing Index Report + + + + + + + Embedded + sql_logo + Fit + 9.25in + 0.5in + 1.75in + 2 + + + + + + + true + true + + + + + ="Dashboard Version: " + Parameters!version_string.Value + + + + + + + 7.875in + 0.25in + 3.125in + 1 + + + + + + + + + + textbox2 + 19 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Value + + + + + + + textbox3 + 18 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + 0.2in + + + + + true + true + + + + + Database ID + + + + + + + textbox5 + 17 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!database_id.Value + + + + + + + database_id + 16 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + 0.2in + + + + + true + true + + + + + Database Name + + + + + + + textbox4 + 15 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!database_name.Value + + + + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + 0.2in + + + + + true + true + + + + + File ID + + + + + + + textbox6 + 13 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!file_id.Value + + + + + + + file_id + 12 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + 0.2in + + + + + true + true + + + + + Page Number + + + + + + + textbox8 + 11 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!page_no.Value + + + + + + + page_no + 10 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + 0.2in + + + + + true + true + + + + + Page Type + + + + + + + textbox7 + 9 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!page_type.Value + + + + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + 0.2in + + + + + true + true + + + + + Object ID + + + + + + + textbox12 + 7 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!object_id.Value + + + + + + + object_id + 6 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + 0.2in + + + + + true + true + + + + + Object Name + + + + + + + textbox15 + 5 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!object_name.Value + + + + + + + object_name + 4 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + 0.2in + + + + + true + true + + + + + Index ID + + + + + + + textbox10 + 3 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!index_id.Value + + + + + + + index_id + 2 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + 0.2in + + + + + true + true + + + + + Level + + + + + + + textbox18 + 1 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!page_level.Value + + + + + + + page_level + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + + + + + + + + + + + After + true + + + + Detail + + + + + + =IIf(Fields!database_name.Value is Nothing, True, False) + + + + + + + + + =IIf(Fields!object_name.Value is Nothing, True, False) + + + + + + Detail_Collection + Output + true + + + + The specified Page ID is not valid or you do not have permissions to view the page. + PAGE_DETAILS + 0.125in + 0.125in + 2in + 10.375in + + + + + + true + true + + + + + wait_resource + + + + + + + true + true + + + + + =Parameters!wait_resource.Value + + + + + + + 1.75in + 0.25in + 5.875in + 1 + + + + ContentsOnly + 2.625in + 0.125in + 0.25in + 7.75in + 1 + + true + view_report_params + + + + + + + + 2.875in + + + + + + + true + true + + + + + ="Report Local Time: " & Globals!ExecutionTime + + + + + + + 0.375in + 0.125in + 0.25in + 2.25in + 1 + + + + Embedded + sql_logo + Fit + 9.125in + 0.5in + 1.75in + 2 + + + + + + + true + true + + + + + ="Dashboard Version: " + Parameters!version_string.Value + + + + + + + 7.75in + 0.25in + 3.125in + 1 + + + + + + + + + + ContentsOnly + 0.65in + 0.1in + 0.375in + 4.7in + + =IIf(avg(Fields!SystemIdle.Value, "CPU_UTILIZATION_HISTORY") < 20 or avg(Fields!SQLProcessUtilization.Value, "CPU_UTILIZATION_HISTORY") > 75 * (Sum(Fields!number_of_schedulers.Value, "MISC_INFO")/Sum(Fields!number_of_cpus.Value, "MISC_INFO")) or max(Fields!SQLProcessUtilization.Value, "CPU_UTILIZATION_HISTORY") > 90 * (Sum(Fields!number_of_schedulers.Value, "MISC_INFO")/Sum(Fields!number_of_cpus.Value, "MISC_INFO")), false, true) + + + + 6pt + + + 6pt + + 0.75pt + + T + + + End Time + + + False + 1 + + False + + 0.75pt + + + NaN + + + False + + 0.75pt + + + NaN + -1 + + + True + + 0.75pt + + + NaN + + + None + + NaN + NaN + true + true + false + + + + + + + NaN + Opposite + NaN + NaN + + + 0.75pt + + + + % CPU + + + True + + True + + 0.75pt + + + NaN + + + False + + 0.75pt + + + NaN + + + True + + 0.75pt + + + NaN + + + None + + true + 0 + 100 + true + true + false + + + + + + + NaN + Opposite + NaN + NaN + + + 0.75pt + + LightGrey + + + + + + + 0.75pt + + Tahoma + 8pt + + Column + + + + + true + + + + + System CPU Utilization + + + + + + + + There is no CPU utilization history available yet; SQL Server may have just been started. Wait at least 60 seconds and refresh the report. + + + CPU_UTILIZATION_HISTORY + 1.03889in + 0.1in + 3.475in + 4.7in + 1 + + + __Upgraded2005__ + __Upgraded2005__ + + + + + + + + + + true + true + + + + + =Sum(Fields!missing_index_count.Value, "MISC_INFO") + + + + + + + missing_index_count + 0.05in + 1.875in + 0.2in + 1in + + + + true + true + + + + + Missing Indexes + + + + + + 2pt + 2pt + 2pt + 2pt + + + + ContentsOnly + 1.25278in + 0.3in + 3.875in + + =IIf(Sum(Fields!missing_index_count.Value, "MISC_INFO") = 0, true, false) + + + + + + + + number_of_databases + 0.8in + 1.875in + 0.2in + 1in + 1 + + + + true + true + + + + + Databases + + + + + + 2pt + 2pt + 2pt + 2pt + + + + true + true + + + + + Active Traces + + + + + + 2pt + 2pt + 2pt + 2pt + + + + true + true + + + + + =Sum(Fields!running_traces.Value, "MISC_INFO") + + + + + + + running_traces + 0.3in + 1.875in + 0.2in + 1in + 4 + + + + true + true + + + + + Miscellaneous Information + + + + + + + true + true + + + + + =Sum(Fields!number_of_xevent_sessions.Value, "MISC_INFO") + + + + + + + 0.55in + 1.875in + 0.2in + 1in + 6 + + + + true + true + + + + + Active Xevent Sessions + + + + + + 2pt + 2pt + 2pt + 2pt + + + + true + true + + + + + Database Storage Report + + + + + + 2pt + 2pt + 2pt + 2pt + + + + ContentsOnly + 6.1in + 5.5in + 1.65833in + 4in + 2 + + + + + + + + + + + =Fields!wait_category.Value + + + + + =sum(Fields!wait_time.Value) + Descending + + + + + + + + + + + + + + + + + + + =IIf(Sum(Fields!wait_time.Value) > 0, Sum(Fields!wait_time.Value), 1) + + + + + 6pt + + 0.75pt + + + + Wait Category + + + False + 1 + + False + + 0.75pt + + + NaN + + + False + + 0.75pt + + + NaN + -1 + + + True + + 0.75pt + + + NaN + + + None + + NaN + NaN + true + true + false + + + + + + + NaN + Opposite + NaN + NaN + + + 0.75pt + + + + Wait Time (ms) + + + False + + True + + 0.75pt + + + NaN + + + False + + 0.75pt + + + NaN + + + True + + 0.75pt + + + NaN + + + None + + true + 0 + true + true + false + + + + + + + NaN + Opposite + NaN + NaN + + + 0.75pt + + LightGrey + + + + + + true + + 0.75pt + + + BottomCenter + Row + + + + + true + + + + + Current Waiting Requests + + + + + + + + There are currently no user requests waiting for a resource. + + + REQUEST_WAIT_STATS + 1.025in + 5.5in + 3.475in + 4.7in + 3 + + + __Upgraded2005__ + __Upgraded2005__ + + + + + + + + true + true + + + + + Current Activity + + + + + + + + + + 1.2in + + + 1.33333in + + + 1.33333in + + + + + 0.2in + + + + + true + true + + + + + + + + + + + + + + + + true + true + + + + + User Requests + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + User Sessions + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + 0.2in + + + + + true + true + + + + + Count + + + + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!num_requests.Value + + + + + + + textbox26 + 13 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =First(Fields!num_sessions.Value, "SESSION_CPU_WAIT_INFO") + + + + + + + textbox31 + 12 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + 0.2in + + + + + true + true + + + + + Elapsed Time (ms) + + + + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!total_elapsed_time.Value + + + + + + + total_elapsed_time + 10 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =First(Fields!total_elapsed_time.Value, "SESSION_CPU_WAIT_INFO") + + + + + + + total_elapsed_time_1 + 9 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + 0.2in + + + + + true + true + + + + + CPU Time (ms) + + + + + + 8pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!cpu_time.Value & " (" & FormatPercent(IIF(Fields!total_elapsed_time.Value > 0, Fields!cpu_time.Value / Fields!total_elapsed_time.Value, 0), 2) & ")" + + + + + + + textbox38 + 7 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =First(Fields!cpu_time.Value, "SESSION_CPU_WAIT_INFO") & " (" & FormatPercent(sum(Fields!cpu_time.Value, "SESSION_CPU_WAIT_INFO")/sum(Fields!total_elapsed_time.Value, "SESSION_CPU_WAIT_INFO"), 2) & ")" + + + + + + + textbox40 + 6 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + 0.2in + + + + + true + true + + + + + Wait Time (ms) + + + + + + 8pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!wait_time.Value & " (" & FormatPercent(IIF(Fields!total_elapsed_time.Value > 0, Fields!wait_time.Value / Fields!total_elapsed_time.Value, 0), 2) & ")" + + + + + + + textbox35 + 4 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =First(Fields!wait_time.Value, "SESSION_CPU_WAIT_INFO") & " (" & FormatPercent(sum(Fields!wait_time.Value, "SESSION_CPU_WAIT_INFO")/sum(Fields!total_elapsed_time.Value, "SESSION_CPU_WAIT_INFO"), 2) & ")" + + + + + + + textbox36 + 3 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + 0.2in + + + + + true + true + + + + + Cache Hit Ratio + + + + + + + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =FormatPercent(Fields!cache_hit_ratio.Value, 3) + + + + + + + cache_hit_ratio + + + + + wait_buffer_io + + + =Parameters!Server.Value + + + =Parameters!version_string.Value + + + + + + + 1 + + + + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =FormatPercent(First(Fields!cache_hit_ratio.Value, "SESSION_CPU_WAIT_INFO"),3) + + + + + + + cache_hit_ratio_1 + + + + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + + + + + + + + + + + + After + true + + + + Detail + + + + + + + + + Detail_Collection + Output + true + + + + REQUEST_CPU_WAIT_INFO + 0.2in + 1.2in + 3.86666in + 1 + + + + + + + + true + true + + + + + System performance may be degraded because of excessive waits happening on the server. Click on a Wait Category data point in the chart below to investigate further. + + + + + + + Embedded + warning2 + image/jpeg + 0.33333in + 0.33333in + 1 + + + + + + + + + true + true + + + + + Waits + + + + + + 2pt + 2pt + 2pt + 2pt + + + + true + true + + + + + Historical Information + + + + + + + true + true + + + + + Expensive Queries + + + + + + + true + true + + + + + By Logical Reads + + + + + + 2pt + 2pt + 2pt + 2pt + + + + true + true + + + + + By Physical Reads + + + + + + 2pt + 2pt + 2pt + 2pt + + + + true + true + + + + + By CPU + + + + + + 2pt + 2pt + 2pt + 2pt + + + + true + true + + + + + By Duration + + + + + + 2pt + 2pt + 2pt + 2pt + + + + true + true + + + + + By Logical Writes + + + + + + 2pt + 2pt + 2pt + 2pt + + + + true + true + + + + + By CLR Time + + + + + + 2pt + 2pt + 2pt + 2pt + + + + true + true + + + + + IO Statistics + + + + + + 2pt + 2pt + 2pt + 2pt + + + + ContentsOnly + 4.6in + 5.5in + 1.4in + 4in + 7 + + + + + + + + true + true + true + + + + + ="NOTE: This SQL Server instance is limited to using only " & Sum(Fields!number_of_schedulers.Value, "MISC_INFO") & " out of " & Sum(Fields!number_of_cpus.Value, "MISC_INFO") & " CPUs. The maximum potential SQL CPU consumption is " & FormatPercent(Sum(Fields!number_of_schedulers.Value, "MISC_INFO")/Sum(Fields!number_of_cpus.Value, "MISC_INFO"), 0) & "." + + + + + + + ContentsOnly + 4.5in + 0.1in + 0.5in + 4.7in + 8 + + =IIf(Sum(Fields!number_of_schedulers.Value, "MISC_INFO") <> Sum(Fields!number_of_cpus.Value, "MISC_INFO"), False, true) + + + + + + + + Embedded + warning2 + image/jpeg + 0.33333in + 0.33333in + 1 + + + + + + + textbox2 + 0.5in + 0.1in + 0.25in + 2.8in + 1 + + + + true + true + + + + + Microsoft SQL Server Performance Dashboard Reports + + + + + + + textbox10 + 0.1in + 0.1in + 0.4in + 6.2in + 2 + + + + + + + + + + true + true + + + + + ="Dashboard Version: " + Parameters!version_string.Value + + + + + + + 0.01389in + 7.825in + 0.25in + 3.125in + 1 + + + + + + + + + + true + true + + + + + Plan Guide Name + + + + + + + true + true + + + + + Plan Guide ID + + + + + + + true + true + + + + + Query Hints + + + + + + + true + true + + + + + =Fields!hints.Value + + + + + + + textbox5 + 1in + 1.5in + 0.2in + 6.25in + 4 + + + + true + true + + + + + Created + + + + + + + true + true + + + + + Last Modified + + + + + + + true + true + + + + + =Fields!modify_date.Value + + + + + + + 0.8in + 1.5in + 0.2in + 6.25in + 7 + + + + true + true + + + + + =Fields!create_date.Value + + + + + + + 0.6in + 1.5in + 0.2in + 6.25in + 8 + + + + true + true + + + + + =Fields!plan_guide_id.Value + + + + + + + 0.4in + 1.5in + 0.2in + 6.25in + 9 + + + + true + true + + + + + =Fields!name.Value + + + + + + + 0.2in + 1.5in + 0.2in + 6.25in + 10 + + + + true + true + + + + + =Parameters!database_name.Value + + + + + + + 1.5in + 0.2in + 6.25in + 11 + + + + true + + + + + + + true + true + + + + + =Parameters!database_name.Value + + + + + + + 1.75in + 0.25in + 6in + 1 + + + + true + true + + + + + plan_guide_name + + + + + + + true + true + + + + + =Parameters!plan_guide_name.Value + + + + + + + 0.25in + 1.75in + 0.25in + 6in + 3 + + + + ContentsOnly + 1.85in + 0.2in + 0.5in + 7.875in + 1 + + true + view_report_params + + + + + + + + 2.35in + + + + + + + true + true + + + + + ="Report Local Time: " & Globals!ExecutionTime + + + + + + + 0.5in + 0.2in + 0.25in + 2.5in + 1 + + + + Embedded + sql_logo + Fit + 9.2in + 0.5in + 1.75in + 2 + + + + + + + true + true + + + + + ="Dashboard Version: " + Parameters!version_string.Value + + + + + + + 7.8in + 0.25in + 3.125in + 1 + + + + + + + + + + true + true + + + + + =IIf(First(Fields!database_name.Value, "QUERY_ATTRIBUTES") is Nothing, First(Fields!database_id.Value, "QUERY_ATTRIBUTES"), First(Fields!database_name.Value, "QUERY_ATTRIBUTES")) + + + + + + + 1.1in + 0.2in + 6.6in + 1 + + + + ContentsOnly + 0.125in + 0.1in + 0.2in + 7.8in + + =IIf(First(Fields!database_name.Value, "QUERY_ATTRIBUTES") is Nothing and First(Fields!database_id.Value, "QUERY_ATTRIBUTES") is Nothing, true, false) + + + + + + + + true + true + + + + + =IIf(First(Fields!query_text.Value, "QUERY_ATTRIBUTES") <> "", First(Fields!query_text.Value, "QUERY_ATTRIBUTES"), "Not available") + + + + + + + + + true + true + + + + + ="SQL Server has identified " & First(Fields!missing_index_count.Value, "SHOWPLAN_ATTRIBUTES") & " missing index" & IIf(Sum(Fields!missing_index_count.Value, "SHOWPLAN_ATTRIBUTES") > 1, "es", "") & " that would benefit this query." + + + + + + + Embedded + warning2 + image/jpeg + 0.33333in + 0.33333in + 1 + + + + + + 2pt + 2pt + 2pt + 2pt + + + + ContentsOnly + 1in + 0.1in + 0.4in + 10.8in + 3 + + =IIf(Sum(Fields!missing_index_count.Value, "SHOWPLAN_ATTRIBUTES") > 0, false, true) + + + + + + + + true + true + + + + + =First(Fields!plan_guide_name.Value, "SHOWPLAN_ATTRIBUTES") + + + + + + 2pt + 2pt + 2pt + 2pt + + + + ContentsOnly + 0.7in + 0.1in + 0.2in + 7.8in + 4 + + =IIf(First(Fields!plan_guide_name.Value, "SHOWPLAN_ATTRIBUTES") <> "", false, true) + + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Estimated Rows + + + + + + + textbox23 + 24 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Estimated IO + + + + + + + textbox29 + 23 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Estimated CPU + + + + + + + textbox27 + 22 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Estimated Subtree Cost + + + + + + + textbox21 + 21 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Estimated Rewinds + + + + + + + textbox28 + 20 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Estimated Rebinds + + + + + + + textbox22 + 19 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Statement Text + + + + + + + textbox19 + 18 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Avg Row Size + + + + + + + textbox20 + 17 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Physical Operator + + + + + + + textbox9 + 16 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Logical Operator + + + + + + + textbox18 + 15 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Node ID + + + + + + + textbox8 + 14 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Parent Node ID + + + + + + + textbox7 + 13 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + 0.2in + + + + + true + true + + + + + =Fields!warnings.Value + + + + + + =IIf(Fields!warnings.Value is nothing, "Transparent", "Yellow") + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!est_rows.Value + + + + + + + est_rows + 11 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!est_io.Value + + + + + + + est_io + 10 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!est_cpu.Value + + + + + + + est_cpu + 9 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!est_subtree_cost.Value + + + + + + + est_subtree_cost + 8 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!est_rewinds.Value + + + + + + + est_rewinds + 7 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!est_rebinds.Value + + + + + + + est_rebinds + 6 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!stmt_text.Value + + + + + + =Str(2 + (Level("table2_Group1") * 20)) & "pt" + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!avg_row_size.Value + + + + + + + avg_row_size + 4 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!physical_op.Value + + + + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!logical_op.Value + + + + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!node_id.Value + + + + + + + node_id + 1 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!parent_node_id.Value + + + + + + + parent_node_id + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + + + + + + =IIf(CBool(First(Fields!warnings_exist.Value, "SHOWPLAN_ATTRIBUTES")) = false, true, false) + + + + + + + + + + + + + + + + + + + + true + After + true + + + + + =Fields!node_id.Value + + =Fields!parent_node_id.Value + + + + + Detail + + + + + Detail_Collection + Output + true + + + + + + =IIf(First(Fields!query_plan.Value, "SHOWPLAN_ATTRIBUTES") is Nothing, "There is no plan that matches the specified plan_handle; the plan may have already aged out of the cache.", "The XML showplan was too complex to convert to the tabular format required by this report. You may view the graphical showplan by doing the following:" & ChrW(13) & ChrW(10) & +"1. Export the report to Excel" & ChrW(13) & ChrW(10) & +"2. Copy the text from the View Showplan XML region to a new text file (Notepad) and save with a .sqlplan extension" & ChrW(13) & ChrW(10) & +"3. Open the .sqlplan file in Management Studio." & ChrW(13) & ChrW(10)) + QUERY_PLAN_SHREDDED + 2.4in + 0.1in + 0.6in + 21in + 5 + + + + + + + true + true + + + + + =Parameters!plan_handle.Value + + + + + + + textbox10 + 1.75in + 0.25in + 6.05in + 1 + + + + true + true + + + + + sql_handle + + + + + + + true + true + + + + + =Parameters!sql_handle.Value + + + + + + + textbox12 + 0.25in + 1.75in + 0.25in + 6.05in + 3 + + + + true + true + + + + + stmt_start_offset + + + + + + + true + true + + + + + =Parameters!stmt_start_offset.Value + + + + + + + textbox3 + 0.5in + 1.75in + 0.25in + 6.05in + 5 + + + + true + true + + + + + stmt_end_offset + + + + + + + true + true + + + + + =Parameters!stmt_end_offset.Value + + + + + + + textbox30 + 0.75in + 1.75in + 0.25in + 6.05in + 7 + + + + ContentsOnly + 3.9in + 0.1in + 1in + 7.9in + 6 + + true + view_report_params + + + + + + + + + + true + true + + + + + =IIf(First(Fields!qualified_object_name.Value, "QUERY_ATTRIBUTES") is nothing, "Object ID", "Object Name") + + + + + + + true + true + + + + + =IIf(First(Fields!qualified_object_name.Value, "QUERY_ATTRIBUTES") is Nothing, First(Fields!object_id.Value, "QUERY_ATTRIBUTES"), First(Fields!qualified_object_name.Value, "QUERY_ATTRIBUTES")) + + + + + + + 1.1in + 0.2in + 6.6in + 1 + + + + ContentsOnly + 0.3in + 0.1in + 0.2in + 7.8in + 8 + + =IIf(First(Fields!qualified_object_name.Value, "QUERY_ATTRIBUTES") is Nothing and First(Fields!object_id.Value, "QUERY_ATTRIBUTES") is Nothing, true, false) + + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Value at Compile Time + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + 0.2in + + + + + true + true + + + + + =Fields!param_name.Value + + + + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!param_compiled_value.Value + + + + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + + + + + + + + + + + true + After + true + + + + Detail + + + + + Detail_Collection + Output + true + + + + PLAN_PARAMETERS + 0.2in + 0.4in + 7.7in + + + + + + + ContentsOnly + 1.5in + 0.1in + 0.6in + 7.8in + 9 + + =IIf(Count(Fields!param_name.Value, "PLAN_PARAMETERS") = 0, true, false) + + + + + + + + true + true + + + + + =First(Fields!query_plan.Value, "SHOWPLAN_ATTRIBUTES") + + + + + + + ContentsOnly + 3.2in + 0.1in + 0.4in + 10.6in + 10 + + + + + + + 4.9in + + + + + + + true + true + + + + + ="Report Local Time: " & Globals!ExecutionTime + + + + + + + 0.4in + 0.1in + 0.25in + 3in + 1 + + + + Embedded + sql_logo + Fit + 9.2in + 0.5in + 1.75in + 2 + + + + + + + true + true + + + + + ="Dashboard Version: " + Parameters!version_string.Value + + + + + + + 7.7in + 0.25in + 3.125in + 1 + + + + + + + + + + + + + 0.625in + + + 6.125in + + + 0.875in + + + 0.875in + + + 0.875in + + + 0.875in + + + 1in + + + 1.25in + + + 1in + + + 1in + + + 1in + + + 1.25in + + + 1in + + + 1in + + + 1in + + + 1.25in + + + 1in + + + 1in + + + 1in + + + 1.25in + + + 1in + + + 1in + + + 1in + + + 1.25in + + + 1in + + + 1in + + + 1in + + + 1.25in + + + 1in + + + 1in + + + 1in + + + + + 0.375in + + + + + true + true + + + + + Query Number + + + + + + + textbox1 + 74 + + + + Gainsboro + + + Gainsboro + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Representative Query + + + + + + + Gainsboro + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Total Executes + + + + + + + Gainsboro + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Cached Query Count + + + + + + + Gainsboro + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + + =Fields!queryplanhashcount.Value + + true + + + + + Unique Plan Count + + + + + + + Gainsboro + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Highest Plan Generation + + + + + + + Gainsboro + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Earliest Plan Cached + + + + + + + Gainsboro + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + + =IIf(Parameters!OrderBy_Criteria.Value = "CPU", true, false) + + true + + + + + Cumulative CPU (ms) + + + + + + + 67 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + 4 + + + + + + + + + true + + =IIf(Parameters!OrderBy_Criteria.Value = "Duration", true, false) + + true + + + + + Cumulative Duration (ms) + + + + + + + 66 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + 4 + + + + + + + + + true + + =IIf(Parameters!OrderBy_Criteria.Value = "Physical Reads", true, false) + + true + + + + + Cumulative Physical Reads + + + + + + + 65 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + 4 + + + + + + + + + true + + =IIf(Parameters!OrderBy_Criteria.Value = "Logical Reads", true, false) + + true + + + + + Cumulative Logical Reads + + + + + + + 64 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + 4 + + + + + + + + + true + + =IIf(Parameters!OrderBy_Criteria.Value = "Logical Writes", true, false) + + true + + + + + Cumulative Logical Writes + + + + + + + 63 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + 4 + + + + + + + + + true + + =IIf(Parameters!OrderBy_Criteria.Value = "CLR Time", true, false) + + true + + + + + Cumulative CLR Time (ms) + + + + + + + 62 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + 4 + + + + + + + + + 0.25in + + + + + true + true + + + + + + + + + + + + Gainsboro + + + Gainsboro + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + + + + + + + + Gainsboro + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + + + + + + + + Gainsboro + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + + + + + + + + Gainsboro + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + + + + + + + + Gainsboro + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + + + + + + + + Gainsboro + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + + + + + + + + Gainsboro + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Total + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Max + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Avg + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Min + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Total + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Max + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Avg + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Min + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Total + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Max + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Avg + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Min + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Total + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Max + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Avg + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Min + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Total + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Max + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Avg + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Min + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Total + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Max + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Avg + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Min + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + 0.25in + + + + + true + true + + + + + =Fields!query_rank.Value + + + + + + + query_rank + 30 + + + + + + + + true + true + + + + + =Fields!query_text.Value + + + + + + + White + 0.8pt + + + Black + 0.8pt + + + Black + + + Black + + =Iif(RowNumber(nothing) Mod 2, "White", "LightSteelBlue") + 2pt + 2pt + 2pt + 2pt + + + true + + + + + + true + true + + + + + =FormatNumber(Fields!execution_count.Value,0) + + + + + + + + + + + true + true + + + + + =Fields!querycount.Value + + + + + + + + + + + true + true + + + + + =Fields!queryplanhashcount.Value + + + + + + + + + + + true + true + + + + + =Fields!max_plan_generation_num.Value + + + + + + + + + + + true + true + + + + + =Fields!earliest_creation_time.Value + + + + + + + + + + + true + true + + + + + =FormatNumber(Fields!total_CPU_time.Value/1000,3) + + + + + + + 23 + + + + + + + + true + true + + + + + =FormatNumber(Fields!max_CPU_time.Value/1000, 3) + + + + + + + 22 + + + + + + + + true + true + + + + + =FormatNumber(Fields!average_CPU_time.Value/1000,3) + + + + + + + 21 + + + + + + + + true + true + + + + + =FormatNumber(Fields!min_CPU_time.Value/1000,3) + + + + + + + 20 + + + + + + + + true + true + + + + + =FormatNumber(Fields!total_elapsed_time.Value/1000, 3) + + + + + + + 19 + + + + + + + + true + true + + + + + =FormatNumber(Fields!max_elapsed_time.Value/1000, 3) + + + + + + + 18 + + + + + + + + true + true + + + + + =FormatNumber(Fields!average_elapsed_time.Value/1000, 3) + + + + + + + 17 + + + + + + + + true + true + + + + + =FormatNumber(Fields!min_elapsed_time.Value/1000, 3) + + + + + + + 16 + + + + + + + + true + true + + + + + =FormatNumber(Fields!total_physical_reads.Value,0) + + + + + + + 15 + + + + + + + + true + true + + + + + =FormatNumber(Fields!max_physical_reads.Value,0) + + + + + + + 14 + + + + + + + + true + true + + + + + =FormatNumber(Fields!average_physical_reads.Value,0) + + + + + + + 13 + + + + + + + + true + true + + + + + =FormatNumber(Fields!min_physical_reads.Value,0) + + + + + + + 12 + + + + + + + + true + true + + + + + =FormatNumber(Fields!total_logical_reads.Value,0) + + + + + + + 11 + + + + + + + + true + true + + + + + =FormatNumber(Fields!max_logical_reads.Value,0) + + + + + + + 10 + + + + + + + + true + true + + + + + =FormatNumber(Fields!average_logical_reads.Value,0) + + + + + + + 9 + + + + + + + + true + true + + + + + =FormatNumber(Fields!min_logical_reads.Value,0) + + + + + + + 8 + + + + + + + + true + true + + + + + =FormatNumber(Fields!total_logical_writes.Value,0) + + + + + + + 7 + + + + + + + + true + true + + + + + =FormatNumber(Fields!max_logical_writes.Value,0) + + + + + + + 6 + + + + + + + + true + true + + + + + =FormatNumber(Fields!average_logical_writes.Value,0) + + + + + + + 5 + + + + + + + + true + true + + + + + =FormatNumber(Fields!min_logical_writes.Value,0) + + + + + + + 4 + + + + + + + + true + true + + + + + =FormatNumber(Fields!total_clr_time.Value/1000, 3) + + + + + + + 3 + + + + + + + + true + true + + + + + =FormatNumber(Fields!max_clr_time.Value/1000, 3) + + + + + + + 2 + + + + + + + + true + true + + + + + =FormatNumber(Fields!average_clr_time.Value/1000, 3) + + + + + + + 1 + + + + + + + + true + true + + + + + =FormatNumber(Fields!min_clr_time.Value/1000, 3) + + + + + + + + + + + + + + + + + + + + + + + + + + + =IIf(Parameters!OrderBy_Criteria.Value = "CPU", false, true) + CumulativeCPU_Header + + + + + =IIf(Parameters!OrderBy_Criteria.Value = "CPU", false, true) + CumulativeCPU_Header + + + + + =IIf(Parameters!OrderBy_Criteria.Value = "CPU", false, true) + CumulativeCPU_Header + + + + + + =IIf(Parameters!OrderBy_Criteria.Value = "Duration", false, true) + CumulativeDuration_Header + + + + + =IIf(Parameters!OrderBy_Criteria.Value = "Duration", false, true) + CumulativeDuration_Header + + + + + =IIf(Parameters!OrderBy_Criteria.Value = "Duration", false, true) + CumulativeDuration_Header + + + + + + =IIf(Parameters!OrderBy_Criteria.Value = "Physical Reads", false, true) + CumulativePhysicalReads_Header + + + + + =IIf(Parameters!OrderBy_Criteria.Value = "Physical Reads", false, true) + CumulativePhysicalReads_Header + + + + + =IIf(Parameters!OrderBy_Criteria.Value = "Physical Reads", false, true) + CumulativePhysicalReads_Header + + + + + + =IIf(Parameters!OrderBy_Criteria.Value = "Logical Reads", false, true) + CumulativeLogicalReads_Header + + + + + =IIf(Parameters!OrderBy_Criteria.Value = "Logical Reads", false, true) + CumulativeLogicalReads_Header + + + + + =IIf(Parameters!OrderBy_Criteria.Value = "Logical Reads", false, true) + CumulativeLogicalReads_Header + + + + + + =IIf(Parameters!OrderBy_Criteria.Value = "Logical Writes", false, true) + CumulativeLogicalWrites_Header + + + + + =IIf(Parameters!OrderBy_Criteria.Value = "Logical Writes", false, true) + CumulativeLogicalWrites_Header + + + + + =IIf(Parameters!OrderBy_Criteria.Value = "Logical Writes", false, true) + CumulativeLogicalWrites_Header + + + + + + =IIf(Parameters!OrderBy_Criteria.Value = "CLR Time", false, true) + CumulativeCLRTime_Header + + + + + =IIf(Parameters!OrderBy_Criteria.Value = "CLR Time", false, true) + CumulativeCLRTime_Header + + + + + =IIf(Parameters!OrderBy_Criteria.Value = "CLR Time", false, true) + CumulativeCLRTime_Header + + + + + + + + true + After + true + + + true + After + true + + + + Detail + + + + + Detail_Collection + Output + true + + + + There are no cached queries matching the specified criteria. + QUERY_STATS_TOP_N1 + 5in + 0.125in + 0.875in + 36.75in + 1 + + =IIf(Count(Fields!query_rank.Value, "QUERY_STATS_TOP_N1") > 0, false, true) + + bmk_high_cpu_query_table + NoOutput + + + + + + + + + + + =Fields!query_rank.Value + + + + + =Fields!query_rank.Value + + + + Output + + + + + + + + + + + + + + + + + =Fields!charted_value.Value + + + + + 6pt + + 0.75pt + + + + Query Number + + + False + 1 + + False + + 0.75pt + + + NaN + + + False + + 0.75pt + + + NaN + -1 + + + True + + 0.75pt + + + NaN + + + None + + NaN + NaN + true + true + false + + + + + + + NaN + Opposite + NaN + NaN + + + 0.75pt + + + + =Switch(Parameters!OrderBy_Criteria.Value = "CPU", "CPU (ms)", Parameters!OrderBy_Criteria.Value = "CLR Time", "CLR Time (ms)", Parameters!OrderBy_Criteria.Value = "Duration", "Duration (ms)", true, Parameters!OrderBy_Criteria.Value) + + + True + + True + + 0.75pt + + + NaN + + + False + + 0.75pt + + + NaN + + + True + + 0.75pt + + + NaN + + + None + + true + 0 + true + true + false + + + + + + + NaN + Opposite + NaN + NaN + + + 0.75pt + + LightGrey + + + + + + true + + 0.75pt + + + RightCenter + Column + + + + + true + + + + + ="Queries with Highest " & Parameters!OrderBy_Criteria.Value + + + + + + + + No rows matched the specified criteria. + + + QUERY_STATS_TOP_N1 + 0.5in + 0.125in + 4.375in + 10.625in + 2 + + + __Upgraded2005__ + __Upgraded2005__ + + + + + + 5.875in + + + + + + + true + true + + + + + ="Report Local Time: " & Globals!ExecutionTime + + + + + + + 0.375in + 0.125in + 0.25in + 2.5in + 1 + + + + Embedded + sql_logo + Fit + 9.125in + 0.5in + 1.75in + 2 + + + + + + + true + true + + + + + ="Dashboard Version: " + Parameters!version_string.Value + + + + + + + 7.75in + 0.25in + 3.125in + 1 + + + + + + + + + + Gainsboro + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + + =Fields!query_text.Value + + true + + + + + Query Text + + + + + + + Gainsboro + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + + =Fields!execution_count.Value + + true + + + + + Executes + + + + + + + textbox32 + 75 + + + + Gainsboro + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + + =Fields!total_worker_time.Value + + true + + + + + CPU Time (ms) + + + + + + + 74 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + 5 + + + + + + + + + + true + + =Fields!total_elapsed_time.Value + + true + + + + + Duration (ms) + + + + + + + 73 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + 5 + + + + + + + + + + true + true + + + + + Wait Time (%) + + + + + + + textbox12 + 72 + + + + Gainsboro + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + + =Fields!total_logical_reads.Value + + true + + + + + Logical Reads + + + + + + + 71 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + 5 + + + + + + + + + + true + + =Fields!total_physical_reads.Value + + true + + + + + Physical Reads + + + + + + + 70 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + 5 + + + + + + + + + + true + + =Fields!total_logical_writes.Value + + true + + + + + Logical Writes + + + + + + + textbox4 + 69 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + 5 + + + + + + + + + + true + + =Fields!total_clr_time.Value + + true + + + + + CLR Time (ms) + + + + + + + textbox28 + 68 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + 5 + + + + + + + + + + 0.25in + + + + + true + true + + + + + + + + + + + + Gainsboro + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + + + + + + + + Gainsboro + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + + + + + + + + textbox36 + 65 + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Total + + + + + + + textbox8 + 64 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Max + + + + + + + textbox9 + 63 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Avg + + + + + + + textbox18 + 62 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Min + + + + + + + textbox15 + 61 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Last + + + + + + + textbox22 + 60 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Total + + + + + + + 59 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Max + + + + + + + 58 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Avg + + + + + + + 57 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Min + + + + + + + 56 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Last + + + + + + + 55 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + + + + + + + + textbox13 + 54 + + + + Gainsboro + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Total + + + + + + + 53 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Max + + + + + + + 52 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Avg + + + + + + + 51 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Min + + + + + + + 50 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Last + + + + + + + 49 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Total + + + + + + + 48 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Max + + + + + + + 47 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Avg + + + + + + + 46 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Min + + + + + + + 45 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Last + + + + + + + 44 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Total + + + + + + + 43 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Max + + + + + + + 42 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Avg + + + + + + + 41 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Min + + + + + + + 40 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Last + + + + + + + 39 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Total + + + + + + + 38 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Max + + + + + + + 37 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Avg + + + + + + + 36 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Min + + + + + + + 35 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Last + + + + + + + 34 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + 0.2in + + + + + true + true + + + + + =Fields!database_name.Value + + + + + + + database_name + 33 + + + + Black + + =Iif(RowNumber(nothing) Mod 2, "White", "LightSteelBlue") + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!query_text.Value + + + + + + + Black + + =Iif(RowNumber(nothing) Mod 2, "White", "LightSteelBlue") + 2pt + 2pt + 2pt + 2pt + + + true + + + + + + true + true + + + + + =Fields!execution_count.Value + + + + + + + execution_count + 31 + + + + Black + + =Iif(RowNumber(nothing) Mod 2, "White", "LightSteelBlue") + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =FormatNumber(Fields!total_worker_time.Value/1000, 3) + + + + + + + Black + + =Iif(RowNumber(nothing) Mod 2, "White", "LightSteelBlue") + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =FormatNumber(Fields!max_worker_time.Value/1000,3) + + + + + + + Black + + =Iif(RowNumber(nothing) Mod 2, "White", "LightSteelBlue") + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =FormatNumber(Fields!total_worker_time.Value/1000/Fields!execution_count.Value, 3) + + + + + + + Black + + =Iif(RowNumber(nothing) Mod 2, "White", "LightSteelBlue") + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =FormatNumber(Fields!min_worker_time.Value/1000, 3) + + + + + + + Black + + =Iif(RowNumber(nothing) Mod 2, "White", "LightSteelBlue") + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =FormatNumber(Fields!last_worker_time.Value/1000, 3) + + + + + + + Black + + =Iif(RowNumber(nothing) Mod 2, "White", "LightSteelBlue") + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =FormatNumber(Fields!total_elapsed_time.Value/1000,3) + + + + + + + Black + + =Iif(RowNumber(nothing) Mod 2, "White", "LightSteelBlue") + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =FormatNumber(Fields!max_elapsed_time.Value/1000, 3) + + + + + + + Black + + =Iif(RowNumber(nothing) Mod 2, "White", "LightSteelBlue") + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =FormatNumber(Fields!total_elapsed_time.Value/Fields!execution_count.Value/1000, 3) + + + + + + + Black + + =Iif(RowNumber(nothing) Mod 2, "White", "LightSteelBlue") + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =FormatNumber(Fields!min_elapsed_time.Value/1000, 3) + + + + + + + Black + + =Iif(RowNumber(nothing) Mod 2, "White", "LightSteelBlue") + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =FormatNumber(Fields!last_elapsed_time.Value/1000, 3) + + + + + + + Black + + =Iif(RowNumber(nothing) Mod 2, "White", "LightSteelBlue") + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =FormatNumber((Fields!total_elapsed_time.Value-Fields!total_worker_time.Value)/Fields!total_elapsed_time.Value * 100, 0) & "%" + + + + + + + Black + + =Iif(RowNumber(nothing) Mod 2, "White", "LightSteelBlue") + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =FormatNumber(Fields!total_logical_reads.Value, 0) + + + + + + + Black + + =Iif(RowNumber(nothing) Mod 2, "White", "LightSteelBlue") + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =FormatNumber(Fields!max_logical_reads.Value, 0) + + + + + + + Black + + =Iif(RowNumber(nothing) Mod 2, "White", "LightSteelBlue") + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =FormatNumber(Fields!total_logical_reads.Value/Fields!execution_count.Value, 0) + + + + + + + Black + + =Iif(RowNumber(nothing) Mod 2, "White", "LightSteelBlue") + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =FormatNumber(Fields!min_logical_reads.Value, 0) + + + + + + + Black + + =Iif(RowNumber(nothing) Mod 2, "White", "LightSteelBlue") + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =FormatNumber(Fields!last_logical_reads.Value, 0) + + + + + + + Black + + =Iif(RowNumber(nothing) Mod 2, "White", "LightSteelBlue") + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =FormatNumber(Fields!total_logical_reads.Value,0) + + + + + + + Black + + =Iif(RowNumber(nothing) Mod 2, "White", "LightSteelBlue") + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =FormatNumber(Fields!max_physical_reads.Value, 0) + + + + + + + Black + + =Iif(RowNumber(nothing) Mod 2, "White", "LightSteelBlue") + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =FormatNumber(Fields!total_physical_reads.Value/Fields!execution_count.Value, 0) + + + + + + + Black + + =Iif(RowNumber(nothing) Mod 2, "White", "LightSteelBlue") + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =FormatNumber(Fields!min_physical_reads.Value, 0) + + + + + + + Black + + =Iif(RowNumber(nothing) Mod 2, "White", "LightSteelBlue") + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =FormatNumber(Fields!last_physical_reads.Value, 0) + + + + + + + Black + + =Iif(RowNumber(nothing) Mod 2, "White", "LightSteelBlue") + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =FormatNumber(Fields!total_logical_writes.Value, 0) + + + + + + + Black + + =Iif(RowNumber(nothing) Mod 2, "White", "LightSteelBlue") + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =FormatNumber(Fields!max_logical_writes.Value, 0) + + + + + + + Black + + =Iif(RowNumber(nothing) Mod 2, "White", "LightSteelBlue") + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =FormatNumber(Fields!total_logical_writes.Value/Fields!execution_count.Value, 0) + + + + + + + Black + + =Iif(RowNumber(nothing) Mod 2, "White", "LightSteelBlue") + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =FormatNumber(Fields!min_logical_writes.Value, 0) + + + + + + + Black + + =Iif(RowNumber(nothing) Mod 2, "White", "LightSteelBlue") + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =FormatNumber(Fields!last_logical_writes.Value, 0) + + + + + + + Black + + =Iif(RowNumber(nothing) Mod 2, "White", "LightSteelBlue") + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =FormatNumber(Fields!total_clr_time.Value/1000, 3) + + + + + + + Black + + =Iif(RowNumber(nothing) Mod 2, "White", "LightSteelBlue") + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =FormatNumber(Fields!max_clr_time.Value/1000, 3) + + + + + + + Black + + =Iif(RowNumber(nothing) Mod 2, "White", "LightSteelBlue") + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =FormatNumber(Fields!total_clr_time.Value/Fields!execution_count.Value/1000, 3) + + + + + + + Black + + =Iif(RowNumber(nothing) Mod 2, "White", "LightSteelBlue") + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =FormatNumber(Fields!min_clr_time.Value/1000, 3) + + + + + + + Black + + =Iif(RowNumber(nothing) Mod 2, "White", "LightSteelBlue") + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =FormatNumber(Fields!last_clr_time.Value/1000, 3) + + + + + + + Black + + =Iif(RowNumber(nothing) Mod 2, "White", "LightSteelBlue") + 2pt + 2pt + 2pt + 2pt + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + After + true + + + true + After + true + + + + Detail + + + + + Detail_Collection + Output + true + + + + QUERY_STATS_DETAILS + 1in + 0.125in + 0.825in + 52.375in + + + + + + true + true + + + + + There have been multiple executions of this query pattern (query hash), but all of the executions are generating the same query plan (query plan hash). This wastes CPU time to compile the query plans and memory to cache them. To improve overall SQL Server performance, consider parameterizing this query (via the application, template plan guide, or forced parameterization). Refer to Books Online for more information on parameterization. + + + + Embedded + warning + FitProportional + 0.5in + 0.5in + 1 + + =Iif(Parameters!Parameterize.Value=true, false, true) + + NoOutput + + + + + + true + true + + + + + query_hash + + + + + + + true + true + + + + + =Parameters!query_hash.Value + + + + + + + 1.75in + 0.25in + 6in + 1 + + + + ContentsOnly + 2.25in + 0.125in + 0.25in + 7.875in + 3 + + true + view_report_params + + + + + + + + 2.5in + + + + + + + true + true + + + + + ="Report Local Time: " & Globals!ExecutionTime + + + + + + + 0.375in + 0.125in + 0.25in + 2.5in + 1 + + + + Embedded + sql_logo + Fit + 9.125in + 0.5in + 1.75in + 2 + + + + + + + true + true + + + + + ="Dashboard Version: " + Parameters!version_string.Value + + + + + + + 7.75in + 0.25in + 3.125in + 1 + + + + + + + + + + Gainsboro + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Query + + + + + + + Gainsboro + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Executes + + + + + + + Gainsboro + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Plan Generation + + + + + + + Gainsboro + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Plan Cached + + + + + + + Gainsboro + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Last Executed + + + + + + + Gainsboro + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + + true + + true + + + + + CPU (ms) + + + + + + + 65 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + 4 + + + + + + + + + true + true + + + + + Duration (ms) + + + + + + + 64 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + 4 + + + + + + + + + true + true + + + + + Physical Reads + + + + + + + 63 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + 4 + + + + + + + + + true + true + + + + + Logical Reads + + + + + + + 62 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + 4 + + + + + + + + + true + true + + + + + Logical Writes + + + + + + + 61 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + 4 + + + + + + + + + true + true + + + + + CLR Time (ms) + + + + + + + 60 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + 4 + + + + + + + + + 0.25in + + + + + true + true + + + + + + + + + + + + Gainsboro + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + + + + + + + + Gainsboro + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + + + + + + + + Gainsboro + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + + + + + + + + Gainsboro + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + + + + + + + + Gainsboro + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + + + + + + + + Gainsboro + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Total + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Max + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Last + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Avg + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Total + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Max + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Last + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Avg + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Total + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Max + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Last + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Avg + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Total + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Max + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Last + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Avg + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Total + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Max + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Last + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Avg + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Total + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Max + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Last + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Avg + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + 0.25in + + + + + true + true + + + + + =Fields!query_rank.Value + + + + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!query_text.Value + + + + + + 2pt + 2pt + 2pt + 2pt + + + true + + + + + + true + true + + + + + =Fields!execution_count.Value + + + + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!plan_generation_num.Value + + + + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!creation_time.Value + + + + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!last_execution_time.Value + + + + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =FormatNumber(Fields!total_worker_time.Value/1000,3) + + + + + + + textbox51 + 23 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =FormatNumber(Fields!max_worker_time.Value/1000, 3) + + + + + + + textbox21 + 22 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =FormatNumber(Fields!last_worker_time.Value/1000, 3) + + + + + + + textbox4 + 21 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =FormatNumber(Fields!total_worker_time.Value/Fields!execution_count.Value / 1000, 3) + + + + + + + 20 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =FormatNumber(Fields!total_elapsed_time.Value/1000, 3) + + + + + + + total_elapsed_time + 19 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =FormatNumber(Fields!max_elapsed_time.Value/1000, 3) + + + + + + + max_elapsed_time + 18 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =FormatNumber(Fields!last_elapsed_time.Value/1000, 3) + + + + + + + last_elapsed_time + 17 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =FormatNumber(Fields!total_elapsed_time.Value/Fields!execution_count.Value/1000, 3) + + + + + + + textbox87 + 16 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!total_physical_reads.Value + + + + + + + total_physical_reads + 15 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!max_physical_reads.Value + + + + + + + max_physical_reads + 14 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!last_physical_reads.Value + + + + + + + last_physical_reads + 13 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =FormatNumber(Fields!total_physical_reads.Value/Fields!execution_count.Value, 0) + + + + + + + textbox96 + 12 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!total_logical_reads.Value + + + + + + + total_logical_reads + 11 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!max_logical_reads.Value + + + + + + + max_logical_reads + 10 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!last_logical_reads.Value + + + + + + + last_logical_reads + 9 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =FormatNumber(Fields!total_logical_reads.Value/Fields!execution_count.Value, 0) + + + + + + + textbox76 + 8 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!total_logical_writes.Value + + + + + + + total_logical_writes + 7 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!max_logical_writes.Value + + + + + + + max_logical_writes + 6 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!last_logical_writes.Value + + + + + + + last_logical_writes + 5 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =FormatNumber(Fields!total_logical_writes.Value/Fields!execution_count.Value, 0) + + + + + + + textbox92 + 4 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =FormatNumber(Fields!total_clr_time.Value/1000, 3) + + + + + + + total_clr_time + 3 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =FormatNumber(Fields!max_clr_time.Value/1000, 3) + + + + + + + max_clr_time + 2 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =FormatNumber(Fields!last_clr_time.Value/1000, 3) + + + + + + + last_clr_time + 1 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =FormatNumber(Fields!total_clr_time.Value/Fields!execution_count.Value/1000, 3) + + + + + + + textbox110 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + + + + + + + + + + + + + cpu_header + + + + + cpu_header + + + + + cpu_header + + + + + + true + duration_header + + + + + true + duration_header + + + + + true + duration_header + + + + + + true + physreads_header + + + + + true + physreads_header + + + + + true + physreads_header + + + + + + true + logreads_header + + + + + true + logreads_header + + + + + true + logreads_header + + + + + + true + logwrites_header + + + + + true + logwrites_header + + + + + true + logwrites_header + + + + + + true + clrtime_header + + + + + true + clrtime_header + + + + + true + clrtime_header + + + + + + + + true + After + true + + + true + After + true + + + + Detail + + + + + Detail_Collection + Output + true + + + + There are no cached queries with last execution time within the specified window. + QUERY_STATS_RECENT_ACTIVITY + 2in + 0.125in + 0.875in + 36.125in + bmk_high_cpu_query_table + + + Tahoma + 8pt + + + + + + + 1in + + + 0.80708in + + + 1.25in + + + 1.25in + + + 1.625in + + + 1.625in + + + 1.625in + + + 2.25in + + + 2.25in + + + + + 0.375in + + + + + true + + =Fields!session_id.Value + + true + + + + + Session ID + + + + + + + textbox16 + 35 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Active Requests + + + + + + + textbox56 + 34 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Estimated Recent CPU (ms) + + + + + + + 33 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Session CPU (ms) + + + + + + + textbox45 + 32 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + + =Fields!login_time.Value + + true + + + + + Login Time + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Last Request Start Time + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Last Request End Time + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Program Name + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Login Name + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + 0.25in + + + + + true + true + + + + + =Fields!session_id.Value + + + + + + + + + + + session_details + + + =Parameters!Server.Value + + + =Fields!session_id.Value + + + =Parameters!version_string.Value + + + + + + + 26 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Count(Fields!request_id.Value) + + + + + + + textbox59 + 25 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =FormatNumber(Min(Fields!session_recent_cpu_est.Value, "table2_Group1") + Sum(Fields!request_recent_cpu_est.Value, "table2_Group1"), 2, true, false, true) + + + + + + + session_recent_cpu_est + 24 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =FormatNumber(Min(Fields!session_cpu_time.Value, "table2_Group1") + Sum(Fields!request_cpu_time.Value, "table2_Group1"), 2, true, false, true) + + + + + + + session_cpu_time + 23 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =First(Fields!login_time.Value) + + + + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =First(Fields!last_request_start_time.Value) + + + + + + + last_request_start_time + 21 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =First(Fields!last_request_end_time.Value) + + + + + + + last_request_end_time + 20 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =First(Fields!program_name.Value) + + + + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =First(Fields!login_name.Value) + + + + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + 0.375in + + + + + true + true + + + + + + + + + + + + textbox34 + 17 + + + + + + + + true + true + + + + + Request ID + + + + + + + textbox44 + 16 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Estimated Recent CPU + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Request CPU (ms) + + + + + + + textbox49 + 14 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Request Start Time + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Request Status + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Command + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + + + + + + + + + + + + true + true + + + + + + + + + + + + + + + + 0.25in + + + + + true + true + + + + + + + + + + + + + + + + true + true + + + + + =Fields!request_id.Value + + + + + + + request_id + 7 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!request_recent_cpu_est.Value + + + + + + + request_recent_cpu_est + 6 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!request_cpu_time.Value + + + + + + + request_cpu_time + 5 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!request_start_time.Value + + + + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!request_status.Value + + + + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!command.Value + + + + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + + + + + + + + + + + + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + After + true + + + + + =Fields!session_id.Value + + + + + =min(Fields!session_recent_cpu_est.Value) + Sum(Fields!request_recent_cpu_est.Value, "table2_Group1") + Descending + + + + + After + true + + + + true + session_id + + After + true + + + + Detail + + + + + true + session_id + + + + Detail_Collection + Output + true + + + + + + No existing sessions appear to have significant recent CPU utilization. + SESSION_REQUEST_ACTIVITY + 0.375in + 0.125in + 1.25in + 13.68208in + 1 + + + + true + true + + + + + This table shows sessions that may have contributed to recent SQL Server CPU utilization. This is based on CPU consumed by current requests and extrapolated CPU usage by each session over its lifetime. This does not include CPU for any sessions which have already logged out. + + + + + + + true + true + + + + + This shows the cached queries which have the highest aggregate CPU usage that are known to have been executed during this time window. + + + + + + + 2.875in + + + + + + + true + true + + + + + ="Report Local Time: " & Globals!ExecutionTime + + + + + + + 0.625in + 0.125in + 0.25in + 2.5in + 1 + + + + true + true + + + + + ="With Activity Since " & Parameters!WithActivitySince.Value + + + + + + + true + true + + + + + =IIf(Parameters!IsUserProcess.Value=0,"Hide System Sessions", "Show System Sessions") + + + + + + 2pt + 2pt + 2pt + 2pt + + + + Embedded + sql_logo + Fit + 9.125in + 0.5in + 1.75in + 4 + + + + + + + true + true + + + + + ="Dashboard Version: " + Parameters!version_string.Value + + + + + + + 7.75in + 0.25in + 3.125in + 1 + + + + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + + =Fields!start_time.Value + + true + + + + + Start Time + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Query Text + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + + =Fields!status.Value + + true + + + + + Status + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + + =Fields!wait_type.Value + + true + + + + + Wait Type + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + + =Fields!wait_time.Value + + true + + + + + Wait Time (ms) + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + + =Fields!wait_resource.Value + + true + + + + + Wait Resource + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + + =Fields!blocking_session_id.Value + + true + + + + + Blocking Session ID + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + + =Fields!cpu_time.Value + + true + + + + + CPU Time (ms) + + + + + + + textbox2 + 23 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + + =Fields!total_elapsed_time.Value + + true + + + + + Total Elapsed Time + + + + + + + textbox12 + 22 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + + =Fields!logical_reads.Value + + true + + + + + Logical Reads + + + + + + + textbox7 + 21 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + + =Fields!reads.Value + + true + + + + + Physical Reads + + + + + + + textbox9 + 20 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + + =Fields!writes.Value + + true + + + + + Writes + + + + + + + textbox5 + 19 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + + =Fields!open_transaction_count.Value + + true + + + + + Transaction Isolation Level + + + + + + + textbox15 + 18 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + + =Fields!granted_query_memory.Value + + true + + + + + Granted Query Memory (KB) + + + + + + + textbox18 + 17 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + + =Fields!executing_managed_code.Value + + true + + + + + Executing Managed Code + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + 0.2in + + + + + true + true + + + + + =Fields!session_id.Value & IIf(Fields!request_id.Value is Nothing, "", ":" & Fields!request_id.Value) + + + + + + 2pt + 2pt + 2pt + 2pt + + + true + + + + + + true + true + + + + + =Fields!start_time.Value + + + + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!query_text.Value + + + + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!status.Value + + + + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!wait_type.Value + + + + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!wait_time.Value + + + + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!wait_resource.Value + + + + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!blocking_session_id.Value + + + + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!cpu_time.Value + + + + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!total_elapsed_time.Value + + + + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!logical_reads.Value + + + + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!reads.Value + + + + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!writes.Value + + + + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!transaction_isolation_level.Value + + + + + + + transaction_isolation_level + 2 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =8192 * Fields!granted_query_memory.Value + + + + + + + granted_query_memory + 1 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!executing_managed_code.Value + + + + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + After + true + + + + Detail + + + + + Detail_Collection + Output + true + + + + REQUEST_DETAILS + 0.125in + 0.125in + 0.575in + 23.75in + + + + 0.7in + + + + + + + true + true + + + + + ="Report Local Time: " & Globals!ExecutionTime + + + + + + + 0.375in + 0.125in + 0.25in + 2.75in + 1 + + + + Embedded + sql_logo + Fit + 8.25in + 0.5in + 1.75in + 2 + + + + + + + true + true + + + + + ="Dashboard Version: " + Parameters!version_string.Value + + + + + + + 4.875in + 0.25in + 3.125in + 1 + + + + + + + + + + textbox14 + 27 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Database ID + + + + + + + textbox21 + 26 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Start Time + + + + + + + textbox16 + 25 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Status + + + + + + + textbox20 + 24 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Query Text + + + + + + + textbox43 + 23 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Logical Reads + + + + + + + textbox50 + 22 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Reads + + + + + + + textbox52 + 21 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Writes + + + + + + + textbox54 + 20 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + CPU (ms) + + + + + + + textbox56 + 19 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Blocking Session ID + + + + + + + textbox45 + 18 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Wait Type + + + + + + + textbox22 + 17 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Wait Time + + + + + + + textbox24 + 16 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Wait Resource + + + + + + + textbox23 + 15 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Transaction Count + + + + + + + textbox25 + 14 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + 0.2in + + + + + true + true + + + + + =Fields!request_id.Value + + + + + + + request_id + 13 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!database_id.Value + + + + + + + database_id + 12 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!start_time.Value + + + + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!status.Value + + + + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!query_text.Value + + + + + + 2pt + 2pt + 2pt + 2pt + + + true + + + + + + true + true + + + + + =Fields!logical_reads.Value + + + + + + + logical_reads_1 + 8 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!reads.Value + + + + + + + reads_1 + 7 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!writes.Value + + + + + + + writes_1 + 6 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!cpu_time.Value + + + + + + + cpu_time_1 + 5 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!blocking_session_id.Value + + + + + + + blocking_session_id + + + + + =IIF(Fields!blocking_session_id.Value <> 0, "session_details", Nothing) + + + =Fields!blocking_session_id.Value + + + =Parameters!version_string.Value + + + + + + + 4 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!wait_type.Value + + + + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!wait_time.Value + + + + + + + wait_time + 2 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!wait_resource.Value + + + + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!open_transaction_count.Value + + + + + + + open_transaction_count + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + After + true + + + + Detail + + + + + Detail_Collection + Output + true + + + + This session currently does not have any requests to the server. + SESSION_REQUESTS + 3.5in + 0.1in + 0.6in + 20.625in + + + + + + + 1.5in + + + 2.4in + + + + + 0.2in + + + + + true + true + + + + + Login Name + + + + + + + + + + + true + true + + + + + =Fields!login_name.Value + + + + + + + login_name_1 + 28 + + + + + + + + 0.2in + + + + + true + true + + + + + Login Time + + + + + + + + + + + true + true + + + + + =Fields!login_time.Value + + + + + + + login_time + 26 + + + + + + + + 0.2in + + + + + true + true + + + + + Host Name + + + + + + + + + + + true + true + + + + + =Fields!host_name.Value + + + + + + + host_name + 24 + + + + + + + + 0.2in + + + + + true + true + + + + + Program Name + + + + + + + + + + + true + true + + + + + =Fields!program_name.Value + + + + + + + program_name_1 + 22 + + + + + + + + 0.2in + + + + + true + true + + + + + NT User Name + + + + + + + + + + + true + true + + + + + =Fields!nt_domain.Value + "\" + Fields!nt_user_name.Value + + + + + + + nt_user_name_1 + 20 + + + + + + + + 0.2in + + + + + true + true + + + + + CPU Time + + + + + + + + + + + true + true + + + + + =Fields!cpu_time.Value + + + + + + + cpu_time + 18 + + + + + + + + 0.2in + + + + + true + true + + + + + Memory Usage + + + + + + + + + + + true + true + + + + + =Fields!memory_usage.Value + + + + + + + memory_usage + 16 + + + + + + + + 0.2in + + + + + true + true + + + + + Total Scheduled Time + + + + + + + + + + + true + true + + + + + =Fields!total_scheduled_time.Value + + + + + + + total_scheduled_time + 14 + + + + + + + + 0.2in + + + + + true + true + + + + + Total Elapsed Time + + + + + + + + + + + true + true + + + + + =Fields!total_elapsed_time.Value + + + + + + + total_elapsed_time + 12 + + + + + + + + 0.2in + + + + + true + true + + + + + Last Request Start + + + + + + + + + + + true + true + + + + + =Fields!last_request_start_time.Value + + + + + + + last_request_start_time + 10 + + + + + + + + 0.2in + + + + + true + true + + + + + Last Request End + + + + + + + + + + + true + true + + + + + =Fields!last_request_end_time.Value + + + + + + + last_request_end_time + 8 + + + + + + + + 0.2in + + + + + true + true + + + + + Reads + + + + + + + + + + + true + true + + + + + =Fields!reads.Value + + + + + + + reads + 6 + + + + + + + + 0.2in + + + + + true + true + + + + + Writes + + + + + + + + + + + true + true + + + + + =Fields!writes.Value + + + + + + + writes + 4 + + + + + + + + 0.2in + + + + + true + true + + + + + Logical Reads + + + + + + + + + + + true + true + + + + + =Fields!logical_reads.Value + + + + + + + logical_reads + 2 + + + + + + + + 0.2in + + + + + true + true + + + + + Last Error + + + + + + + + + + + true + true + + + + + =Fields!prev_error.Value + + + + + + + prev_error + + + + + + + + + + + + + + + + + + + Detail + + + + + + + + + + + + + + + + + + + Detail_Collection + Output + true + + + + There is no longer a session matching the specified session_id. + SESSION_DATA + 0.1in + 0.1in + 3in + 3.9in + 1 + + + + + + + 2in + + + 2in + + + + + 0.2in + + + + + true + true + + + + + Text Size + + + + + + + + + + + true + true + + + + + =Fields!text_size.Value + + + + + + + text_size + 28 + + + + + + + + 0.2in + + + + + true + true + + + + + Language + + + + + + + + + + + true + true + + + + + =Fields!language.Value + + + + + + + language + 26 + + + + + + + + 0.2in + + + + + true + true + + + + + Date Format + + + + + + + + + + + true + true + + + + + =Fields!date_format.Value + + + + + + + date_format + 24 + + + + + + + + 0.2in + + + + + true + true + + + + + Date First + + + + + + + + + + + true + true + + + + + =Fields!date_first.Value + + + + + + + date_first + 22 + + + + + + + + 0.2in + + + + + true + true + + + + + Quoted Identifier + + + + + + + + + + + true + true + + + + + =Fields!quoted_identifier.Value + + + + + + + quoted_identifier_1 + 20 + + + + + + + + 0.2in + + + + + true + true + + + + + Arithabort + + + + + + + + + + + true + true + + + + + =Fields!arithabort.Value + + + + + + + arithabort + 18 + + + + + + + + 0.2in + + + + + true + true + + + + + ANSI NULL Default On + + + + + + + + + + + true + true + + + + + =Fields!ansi_null_dflt_on.Value + + + + + + + ansi_null_dflt_on + 16 + + + + + + + + 0.2in + + + + + true + true + + + + + ANSI Defaults + + + + + + + + + + + true + true + + + + + =Fields!ansi_defaults.Value + + + + + + + ansi_defaults + 14 + + + + + + + + 0.2in + + + + + true + true + + + + + ANSI Warnings + + + + + + + + + + + true + true + + + + + =Fields!ansi_warnings.Value + + + + + + + ansi_warnings + 12 + + + + + + + + 0.2in + + + + + true + true + + + + + ANSI Padding + + + + + + + + + + + true + true + + + + + =Fields!ansi_padding.Value + + + + + + + ansi_padding + 10 + + + + + + + + 0.2in + + + + + true + true + + + + + ANSI Nulls + + + + + + + + + + + true + true + + + + + =Fields!ansi_nulls.Value + + + + + + + ansi_nulls + 8 + + + + + + + + 0.2in + + + + + true + true + + + + + Concat Null Yields Null + + + + + + + + + + + true + true + + + + + =Fields!concat_null_yields_null.Value + + + + + + + concat_null_yields_null + 6 + + + + + + + + 0.2in + + + + + true + true + + + + + Transaction Isolation Level + + + + + + + + + + + true + true + + + + + =Fields!transaction_isolation_level.Value + + + + + + + transaction_isolation_level + 4 + + + + + + + + 0.2in + + + + + true + true + + + + + Lock Timeout + + + + + + + + + + + true + true + + + + + =Fields!lock_timeout.Value + + + + + + + lock_timeout + 2 + + + + + + + + 0.2in + + + + + true + true + + + + + Deadlock Priority + + + + + + + + + + + true + true + + + + + =Fields!deadlock_priority.Value + + + + + + + deadlock_priority + + + + + + + + + + + + + + + + + + + Detail + + + + + + + + + + + + + + + + + + + Detail_Collection + Output + true + + + + SESSION_DATA + 0.3in + 4.7in + 3in + 4in + 2 + + + + true + true + + + + + SET options: + + + + + + + + + + + + 3.125in + + + 3.125in + + + 5.875in + + + + + 0.2in + + + + + true + true + + + + + Database Name + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Object Name + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Query Text + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + 0.2in + + + + + true + true + + + + + =Fields!database_name.Value + + + + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!object_name.Value + + + + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!last_query.Value + + + + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + + + + + + =IIf(First(Fields!database_name.Value, "LAST_BATCH_FOR_IDLE_SESSION") is Nothing, true, false) + + + + + =IIf(First(Fields!object_name.Value, "LAST_BATCH_FOR_IDLE_SESSION") is Nothing, true, false) + + + + + + + + + After + true + + + + Detail + + + + + Detail_Collection + Output + true + + + + LAST_BATCH_FOR_IDLE_SESSION + 0.2in + 0.4in + 12.125in + + + + true + true + + + + + Last query executed by this session: + + + + + + + ContentsOnly + 4.3in + 0.1in + 0.6in + 12.225in + 4 + + =IIf(Count(Fields!last_query.Value, "LAST_BATCH_FOR_IDLE_SESSION") > 0, false, true) + + + + + + + + true + true + + + + + ="Report Local Time: " & Globals!ExecutionTime + + + + + + + textbox2 + 0.4in + 0.1in + 0.25in + 2.6in + 1 + + + + Embedded + sql_logo + Fit + 9.2in + 0.5in + 1.75in + 2 + + + + + + + true + true + + + + + ="Dashboard Version: " + Parameters!version_string.Value + + + + + + + 7.9in + 0.25in + 3.125in + 1 + + + + + + + + + + textbox1 + 29 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + + =Fields!status.Value + + true + + + + + Status + + + + + + + textbox7 + 28 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + + =Fields!cpu_time.Value + + true + + + + + CPU Time (ms) + + + + + + + textbox13 + 27 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + + =Fields!memory_usage.Value + + true + + + + + Memory Usage + + + + + + + textbox15 + 26 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + + =Fields!logical_reads.Value + + true + + + + + Logical Reads + + + + + + + textbox8 + 25 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + + =Fields!reads.Value + + true + + + + + Physical Reads + + + + + + + textbox18 + 24 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + + =Fields!writes.Value + + true + + + + + Writes + + + + + + + textbox14 + 23 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + + =Fields!login_name.Value + + true + + + + + Login Name + + + + + + + textbox5 + 22 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + + =Fields!program_name.Value + + true + + + + + Program Name + + + + + + + textbox4 + 21 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + + =Fields!host_name.Value + + true + + + + + Host Name + + + + + + + textbox3 + 20 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + + =Fields!last_request_start_time.Value + + true + + + + + Last Request Start Time + + + + + + + textbox22 + 19 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + + =Fields!last_request_end_time.Value + + true + + + + + Last Request End Time + + + + + + + textbox20 + 18 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + User Process + + + + + + + textbox19 + 17 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + NT Domain + + + + + + + textbox11 + 16 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + + =Fields!nt_user_name.Value + + true + + + + + NT User Name + + + + + + + textbox9 + 15 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + 0.2in + + + + + true + true + + + + + =Fields!session_id.Value + + + + + + + session_id + + + + + session_details + + + =Parameters!Server.Value + + + =Fields!session_id.Value + + + =Parameters!version_string.Value + + + + + + + 14 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!status.Value + + + + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!cpu_time.Value + + + + + + + cpu_time + 12 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!memory_usage.Value + + + + + + + memory_usage + 11 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!logical_reads.Value + + + + + + + logical_reads + 10 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!reads.Value + + + + + + + reads + 9 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!writes.Value + + + + + + + writes + 8 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!login_name.Value + + + + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!program_name.Value + + + + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!host_name.Value + + + + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!last_request_start_time.Value + + + + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!last_request_end_time.Value + + + + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!is_user_process.Value + + + + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!nt_domain.Value + + + + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!nt_user_name.Value + + + + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + After + true + + + + Detail + + + + + Detail_Collection + Output + true + + + + SESSION_DETAILS + 0.125in + 0.4in + 21.16667in + + + + 0.4in + + + + + + + true + true + + + + + ="Report Local Time: " & Globals!ExecutionTime + + + + + + + textbox2 + 0.375in + 0.125in + 0.25in + 3in + 1 + + + + Embedded + sql_logo + Fit + 9.125in + 0.5in + 1.75in + 2 + + + + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + + + true + true + + + + + ="Dashboard Version: " + Parameters!version_string.Value + + + + + + + 7.75in + 0.25in + 3.125in + 1 + + + + + + + + + + true + true + + + + + =First(Fields!max_files.Value) + + + + + + + 1in + 1in + 0.2in + 2in + 1 + + + + true + true + + + + + =First(Fields!last_event_time.Value) + + + + + + + 0.8in + 1in + 0.2in + 2in + 2 + + + + true + true + + + + + =First(Fields!stop_time.Value) + + + + + + + 0.6in + 1in + 0.2in + 2in + 3 + + + + true + true + + + + + =First(Fields!start_time.Value) + + + + + + + 0.4in + 1in + 0.2in + 2in + 4 + + + + true + true + + + + + =IIf(Fields!is_rowset.Value = 0, Fields!path.Value, "rowset trace: client/GUI traces can negatively impact server performance") + + + + + + + 0.2in + 1in + 0.2in + 6.875in + 5 + + + + true + true + + + + + Trace ID + + + + + + + true + true + + + + + =Fields!trace_id.Value + + + + + + + trace_id_1 + 1in + 0.2in + 2.875in + 7 + + + + + + + 1in + + + 1.125in + + + 5.75in + + + + + 0.2in + + + + + true + true + + + + + Event ID + + + + + + + textbox19 + 9 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Event Name + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + 2 + + + + + + + 0.2in + + + + + true + true + + + + + =Fields!trace_event_id.Value + + + + + + + trace_event_id + 7 + + + =IIf(CBool(Fields!expensive_event.Value) = true, "Yellow", "White") + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!trace_event_name.Value + + + + + + =IIf(CBool(Fields!expensive_event.Value) = true, "Yellow", "White") + 2pt + 2pt + 2pt + 2pt + + + 2 + + + + + + + 0.2in + + + + + true + true + + + + + + + + + + + + textbox23 + 5 + + + =IIf(CBool(Fields!expensive_event.Value) = true, "Yellow", "White") + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Column ID + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Column Name + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + 0.2in + + + + + true + true + + + + + + + + + + + + + + + + true + true + + + + + =Fields!trace_column_id.Value + + + + + + + trace_column_id + 1 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!trace_column_name.Value + + + + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + + + + + + + + + + + + After + true + + + + + =Fields!trace_event_name.Value + + + + + =Fields!trace_event_id.Value + + + + + After + true + + + + true + trace_event_id + + After + true + + + + Detail + + + + + true + trace_event_id + + + + Detail_Collection + Output + true + + + + + + TRACE_EVENTS_COLUMNS + 2.1in + 0.8in + 7.875in + 8 + + true + details + + + + + + + + 3in + 0in + 7.875in + 10 + + 2pt + + + + + true + true + + + + + =First(Fields!buffer_count.Value) + + + + + + + buffer_count + 0.4in + 5.625in + 0.2in + 2.25in + 11 + + + + true + true + + + + + =First(Fields!event_count.Value) + + + + + + + event_count + 0.8in + 5.625in + 0.2in + 2.25in + 12 + + + + true + true + + + + + Trace Buffers + + + + + + + true + true + + + + + Events Traced + + + + + + + true + true + + + + + Trace Buffer Size + + + + + + + true + true + + + + + =First(Fields!buffer_size.Value) & " KB" + + + + + + + buffer_size + 0.6in + 5.625in + 0.2in + 2.25in + 16 + + + + true + true + + + + + =IIf(First(Fields!max_size.Value) is Nothing, "", First(Fields!max_size.Value) & " MB") + + + + + + + max_size + 1in + 5.625in + 0.2in + 2.25in + 17 + + + + + + Embedded + warning + 0.33333in + 0.33333in + + + + + + + ContentsOnly + 1.3in + 0.5in + 7.875in + 18 + + =IIf(Max(Fields!expensive_event.Value, "list1_Details_Group") = true, false, true) + + + + + + + + true + true + + + + + =IIf(Fields!status.Value = 0, "Stopped", "Running") + + + + + + + textbox3 + 5.625in + 0.2in + 2.25in + 20 + + + + true + true + + + + + Max Files + + + + + + + true + true + + + + + Last Event + + + + + + + true + true + + + + + Stop Time + + + + + + + true + true + + + + + Start Time + + + + + + + true + true + + + + + File Name + + + + + + + true + + + + + + + 0.1in + 1.7in + 0.2in + 2.2in + + + + true + true + + + + + =First(Fields!target_name.Value) + + + + + + + 0.3in + 1.7in + 0.2in + 2.2in + 1 + + + + true + true + + + + + =First(Fields!execution_count.Value) + + + + + + + 0.7in + 1.7in + 0.2in + 2.2in + 2 + + + + true + true + + + + + Session Name + + + + + + + true + true + + + + + Session Target + + + + + + + true + true + + + + + Target Executions + + + + + + + true + true + + + + + =First(Fields!execution_duration_ms.Value) + + + + + + + 0.9in + 1.7in + 0.2in + 2.2in + 6 + + + + true + true + + + + + Execution Duration (ms) + + + + + + + + + + 2.1in + + + 2.1in + + + + + 0.2in + + + + + true + true + + + + + Event Name + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Action + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + 0.2in + + + + + true + true + + + + + =Fields!event_name.value + + + + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + + + + + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + 0.2in + + + + + true + true + + + + + + + + + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!action_name.value + + + + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + + + + + + + + + + + After + true + + + + + =Fields!event_name.Value + + + + + After + true + + + + Detail + + + + + true + Xevent_name + + + + Detail_Collection + Output + true + + + + + + 1.4in + 0.1in + 0.6in + 4.2in + 8 + + true + EventsActionsTextBox + + + + + + + true + true + + + + + Configured Events: + + + + + + + 2.3in + 0in + 7.875in + 10 + + 2pt + + + + + true + true + + + + + =FormatNumber(First(Fields!total_buffer_size.Value)/1000,0) & " KB" + + + + + + + 0.3in + 5.4in + 0.2in + 2.2in + 11 + + + + true + true + + + + + Total Buffer Size + + + + + + + true + true + + + + + =First(Fields!buffer_policy_desc.Value) + + + + + + + 0.5in + 5.4in + 0.2in + 2.2in + 13 + + + + true + true + + + + + Buffer Policy + + + + + + + true + true + + + + + =FormatNumber(First(Fields!dropped_event_count.Value),0) + + + + + + + 0.7in + 5.4in + 0.2in + 2.2in + 15 + + + + true + true + + + + + Dropped Events + + + + + + + true + true + + + + + =First(Fields!create_time.Value) + + + + + + + 0.5in + 1.7in + 0.2in + 2.2in + 17 + + + + true + true + + + + + Session Creation Time + + + + + + + true + + + 8.65in + + + 0.7in + true + true + + + true + true + + + + + ="Report Local Time: " & Globals!ExecutionTime + + + + + + + 0.4in + 0.1in + 0.25in + 2.5in + + + + true + true + + + + + =iif(Parameters!TracingType.Value=1, "Active Traces", "Active Extended Event Sessions") + + + + + + + Embedded + sql_logo + Fit + 6.9in + 0.5in + 1.75in + 2 + + + + + + + true + true + + + + + ="Dashboard Version: " + Parameters!version_string.Value + + + + + + + 4.875in + 0.25in + 3.125in + 1 + + + + + + + + + + true + true + + + + + =Sum(Fields!wait_time.Value) & " ms" + + + + + + + 0.25in + 5.5in + 0.25in + 2.375in + 1 + + + + true + true + + + + + =Count(Fields!session_id.Value) - 1 + + + + + + + 5.5in + 0.25in + 1in + 2 + + + + true + true + + + + + Blocked Sessions + + + + + + + true + true + + + + + Wait Time + + + + + + + true + true + + + + + Head Blocker + + + + + + + true + true + + + + + =Fields!head_wait_resource.Value + + + + + + + true + true + + + + + =Fields!session_id.Value + + + + + + + session_id_1 + 1.375in + 0.25in + 2.625in + 7 + + + + true + true + + + + + Wait Resource + + + + + + + true + true + + + + + =Fields!program_name.Value + + + + + + + true + true + + + + + Program Name + + + + + + + 3.375in + 0in + 15.375in + 11 + + 2pt + + + + + + + Embedded + warning_1 + 0.33333in + 0.33333in + + + + + + + ContentsOnly + 1in + 0.375in + 7.875in + 12 + + =IIf(Fields!session_or_request_status.Value = "idle" and Fields!seconds_active_idle.Value > 15, False, true) + + + + + + + + true + true + + + + + =First(Fields!query_text.Value) + + + + + + + query_2 + + + + + query_plan + + + =Parameters!Server.Value + + + =Fields!plan_handle.Value + + + =Fields!sql_handle.Value + + + =Fields!statement_start_offset.Value + + + =Fields!statement_end_offset.Value + + + =Parameters!version_string.Value + + + + + + + 1.375in + 0.25in + 6.375in + 1 + + + 2pt + 2pt + 2pt + 2pt + + + + ContentsOnly + 0.75in + 0.25in + 7.875in + 13 + + =IIf(First(Fields!query_text.Value) is Nothing, true, false) + + + + + + + + true + true + + + + + =Fields!open_transaction_count.Value + + + + + + + open_transaction_count + 0.5in + 1.375in + 0.25in + 2.625in + 15 + + + + + + Embedded + warning_1 + 0.33333in + 0.33333in + + + + + + + ContentsOnly + 1.375in + 0.375in + 7.875in + 16 + + =IIf(Fields!session_or_request_status.Value <> "idle" and Fields!seconds_active_idle.Value > 60, False, true) + + + + + + + + 39 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Status + + + + + + + 38 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Transaction Name + + + + + + + textbox26 + 37 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Isolation Level + + + + + + + textbox34 + 36 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Wait Type + + + + + + + 35 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Wait Time (ms) + + + + + + + 34 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Wait Resource + + + + + + + 33 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + 3 + + + + + + + + true + true + + + + + Query Text + + + + + + + 32 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + 3 + + + + + + + + 0.2in + + + + + true + true + + + + + =Fields!session_id.Value & IIf(Fields!request_id.Value is Nothing, "", ":" & Fields!request_id.Value) + + + + + + + + + + + session_details + + + =Parameters!Server.Value + + + =Fields!session_id.Value + + + =Parameters!version_string.Value + + + + + + + 31 + + + =((Fields!tree_level.Value * 10) + 2).ToString() & "pt" + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!session_or_request_status.Value + + + + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!transaction_name.Value + + + + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Switch(Fields!transaction_isolation_level.Value = 0, "Read Committed", + Fields!transaction_isolation_level.Value = 1, "Read Uncommitted", + Fields!transaction_isolation_level.Value = 2, "Read Committed", + Fields!transaction_isolation_level.Value = 3, "Repeatable Read", + Fields!transaction_isolation_level.Value = 4, "Serializable", + Fields!transaction_isolation_level.Value = 5, "Snapshot", + true, Fields!transaction_isolation_level.Value.ToString()) + + + + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!wait_type.Value + + + + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!wait_time.Value + + + + + + + 26 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!wait_resource.Value + + + + + + 2pt + 2pt + 2pt + 2pt + + + 3 + + + + + + + + true + true + + + + + =Fields!query_text.Value + + + + + + 2pt + 2pt + 2pt + 2pt + + + 3 + true + + + + + + + + 0.375in + + + + + true + true + + + + + + + + + + + + textbox10 + 23 + + + + + + + + true + true + + + + + Transaction ID + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Transaction Begin Time + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Transaction State + + + + + + + 20 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Transaction Type + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Enlist Count + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + User Transaction + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Local + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Enlisted + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Bound + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + DTC State + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + DTC Isolation Level + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + 0.2in + + + + + true + true + + + + + + + + + + + + + + + + true + true + + + + + =Fields!transaction_id.Value + + + + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!transaction_begin_time.Value + + + + + + + transaction_begin_time + 9 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Switch(Fields!transaction_state.Value = 0, "Initializing", +Fields!transaction_state.Value = 1, "Initialized", +Fields!transaction_state.Value = 2, "Active", +Fields!transaction_state.Value = 3, "Ended", +Fields!transaction_state.Value = 4, "Preparing", +Fields!transaction_state.Value = 5, "Prepared", +Fields!transaction_state.Value = 6, "Committed", +Fields!transaction_state.Value = 7, "Rolling back", +Fields!transaction_state.Value = 8, "Rolled back", +true, Fields!transaction_state.Value.ToString()) + + + + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Switch(Fields!transaction_type.Value = 1, "Read/Write", +Fields!transaction_type.Value = 2, "Read only", +Fields!transaction_type.Value = 3, "System", +Fields!transaction_type.Value = 4, "Distributed", +true, Fields!transaction_type.Value.ToString()) + + + + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!enlist_count.Value + + + + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!is_user_transaction.Value + + + + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!is_local.Value + + + + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!is_enlisted.Value + + + + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!is_bound.Value + + + + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Switch(Fields!dtc_state.Value = 1, "Active", +Fields!dtc_state.Value = 2, "Prepared", +Fields!dtc_state.Value = 3, "Committed", +Fields!dtc_state.Value = 4, "Aborted", +Fields!dtc_state.Value = 5, "Recovered", +true, Fields!dtc_state.Value.ToString()) + + + + + + + dtc_state_1 + 1 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!dtc_isolation_level.Value + + + + + + + dtc_isolation_level + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + + + + + + + + + + + + + + + + + + + + + After + true + + + + + =Fields!session_id.Value + + =Fields!blocking_session_id.Value + + + + + Detail + + + + + + true + transaction_name + + + + + true + transaction_name + + + + Detail_Collection + Output + true + + + + + + BLOCKING + 2.125in + 1.15in + 16in + 17 + + true + view_details + + + + + + + + 3.875in + + + + + + + true + true + + + + + ="Report Local Time: " & Globals!ExecutionTime + + + + + + + 0.375in + 0.125in + 0.25in + 2.625in + 1 + + + + Embedded + sql_logo + Fit + 9.125in + 0.5in + 1.75in + 2 + + + + + + + true + true + + + + + ="Dashboard Version: " + Parameters!version_string.Value + + + + + + + 7.625in + 0.25in + 3.125in + 1 + + + + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + 2 + + + + + + + true + true + + + + + # of Waiters + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Wait Time (ms) + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + + + + + + + + + + + + true + true + + + + + + + + + + + + + + + + 0.2in + + + + + true + true + + + + + =Fields!wait_resource.Value + + + + + + 2pt + 2pt + 2pt + 2pt + + + 2 + + + + + + + true + true + + + + + =Count(Fields!session_id.Value) + + + + + + + session_id_1 + 15 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Sum(Fields!wait_time.Value) + + + + + + + wait_time_2 + 14 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + + + + + + + + + + + + true + true + + + + + + + + + + + + + + + + 0.375in + + + + + true + true + + + + + + + + + + + + + + + + true + true + + + + + Session ID : Request ID + + + + + + + textbox7 + 10 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Wait Type + + + + + + + wait_time + 9 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Wait Time (ms) + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Blocking Session ID + + + + + + + textbox6 + 7 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Query Text + + + + + + + textbox40 + 6 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + 0.2in + + + + + true + true + + + + + + + + + + + + + + + + true + true + + + + + =Fields!session_id.Value & IIf(Fields!request_id.Value is Nothing, "", ":" & Fields!request_id.Value) + + + + + + + session_id + + + + + session_details + + + =Parameters!Server.Value + + + =Fields!session_id.Value + + + =Parameters!version_string.Value + + + + + + + 4 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!wait_type.Value + + + + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!wait_time.Value + + + + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!blocking_session_id.Value + + + + + + + blocking_session_id + + + + + session_details + + + =Parameters!Server.Value + + + =Fields!blocking_session_id.Value + + + =Parameters!version_string.Value + + + + + + + 1 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!query_text.Value + + + + + + + textbox41 + + + + + query_plan + + + =Parameters!Server.Value + + + =Fields!plan_handle.Value + + + =Fields!sql_handle.Value + + + =Fields!statement_start_offset.Value + + + =Fields!statement_end_offset.Value + + + =Parameters!version_string.Value + + + + + + + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + + + + + + + + + + + + + + + true + After + true + + + + + =Fields!wait_resource.Value + + + + + =Sum(Fields!wait_time.Value) + Descending + + + + + After + true + + + + true + wait_resource + + After + true + + + + Detail + + + + + true + wait_resource + + + + Detail_Collection + Output + true + + + + + + There are currently no requests waiting on a buffer latch. + REQUEST_IO_WAITS + 0.375in + 0.125in + 1.025in + 11.875in + + + + + + + 1in + + + 1.125in + + + 1.125in + + + 1in + + + 1.125in + + + 1.125in + + + 1.125in + + + 2.75in + + + 5.75in + + + + + 0.375in + + + + + true + + =Fields!session_id.Value + + true + + + + + Session ID : Request ID + + + + + + + textbox8 + 17 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + + =Fields!cache_hit_ratio.Value + + true + + + + + Cache Hit Ratio + + + + + + + textbox21 + 16 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + + =Fields!reads.Value + + true + + + + + Physical Reads + + + + + + + textbox9 + 15 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + + =Fields!logical_reads.Value + + true + + + + + Logical Reads + + + + + + + textbox19 + 14 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + + =Fields!writes.Value + + true + + + + + Writes + + + + + + + textbox18 + 13 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Wait Type + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Wait Time + + + + + + + textbox15 + 11 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Wait Resource + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Query Text + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + 0.2in + + + + + true + true + + + + + =Fields!session_id.Value & IIf(Fields!request_id.Value is Nothing, "", ":" & Fields!request_id.Value) + + + + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =FormatPercent(Fields!cache_hit_ratio.Value, 3) + + + + + + + cache_hit_ratio + 7 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!reads.Value + + + + + + + reads + 6 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!logical_reads.Value + + + + + + + logical_reads + 5 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!writes.Value + + + + + + + writes + 4 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!wait_type.Value + + + + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!wait_time.Value + + + + + + + wait_time_1 + 2 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!wait_resource.Value + + + + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!query_text.Value + + + + + + 2pt + 2pt + 2pt + 2pt + + + true + + + + + + + + + + + + + + + + + + + + + + + true + After + true + + + + Detail + + + + =Fields!reads.Value + Descending + + + + + + Detail_Collection + Output + true + + + + There are currently no user requests which have performed any physical reads. + LARGEST_IO_REQUESTS + 1.75in + 0.125in + 0.575in + 16.125in + 1 + + + + true + true + + + + + This following table shows all current requests that are waiting on a buffer IO, grouped by the page they are waiting on. + + + + + + + true + true + + + + + This following table shows the current requests that have performed the most physical IO. + + + + + + + 2.325in + + + + + + + true + true + + + + + ="Report Local Time: " & Globals!ExecutionTime + + + + + + + 0.375in + 0.125in + 0.25in + 3in + 1 + + + + Embedded + sql_logo + Fit + 9.125in + 0.5in + 1.75in + 2 + + + + + + + true + true + + + + + ="Dashboard Version: " + Parameters!version_string.Value + + + + + + + 7.625in + 0.25in + 3.125in + 1 + + + + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + 2 + + + + + + + true + true + + + + + # of Waiters + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Wait Time (ms) + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + + + + + + + + + + + + true + true + + + + + + + + + + + + + + + + 0.2in + + + + + true + true + + + + + =Fields!wait_resource.Value + + + + + + 2pt + 2pt + 2pt + 2pt + + + 2 + + + + + + + true + true + + + + + =Count(Fields!session_id.Value) + + + + + + + session_id_1 + 15 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Sum(Fields!wait_time.Value) + + + + + + + wait_time_2 + 14 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + + + + + + + + + + + + true + true + + + + + + + + + + + + + + + + 0.375in + + + + + true + true + + + + + + + + + + + + + + + + true + true + + + + + Session ID: Request ID + + + + + + + textbox7 + 10 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Wait Type + + + + + + + wait_time + 9 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Wait Time (ms) + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Blocking Session ID + + + + + + + textbox6 + 7 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Query Text + + + + + + + textbox40 + 6 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + 0.2in + + + + + true + true + + + + + + + + + + + + + + + + true + true + + + + + =Fields!session_id.Value & IIf(Fields!request_id.Value is Nothing, "", ":" & Fields!request_id.Value) + + + + + + + session_id + + + + + session_details + + + =Parameters!Server.Value + + + =Fields!session_id.Value + + + =Parameters!version_string.Value + + + + + + + 4 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!wait_type.Value + + + + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!wait_time.Value + + + + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!blocking_session_id.Value + + + + + + + blocking_session_id + + + + + session_details + + + =Parameters!Server.Value + + + =Fields!blocking_session_id.Value + + + =Parameters!version_string.Value + + + + + + + 1 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!query_text.Value + + + + + + + textbox41 + + + + + query_plan + + + =Parameters!Server.Value + + + =Fields!plan_handle.Value + + + =Fields!sql_handle.Value + + + =Fields!statement_start_offset.Value + + + =Fields!statement_end_offset.Value + + + =Parameters!version_string.Value + + + + + + + + + 2pt + 2pt + 2pt + 2pt + + + true + + + + + + + + + + + + + + + + + + + + true + After + true + + + + + =Fields!wait_resource.Value + + + + + =Sum(Fields!wait_time.Value) + Descending + + + + + After + true + + + + true + wait_resource + + After + true + + + + Detail + + + + + true + wait_resource + + + + Detail_Collection + Output + true + + + + + + There are currently no sessions waiting on a buffer latch. + REQUEST_IO_WAITS + 0.5in + 0.125in + 1.025in + 10.125in + + + + true + true + + + + + Buffer latches are used for in-memory synchronization while accessing a particular database page. This reports shows all sessions waiting on a buffer latch, grouped by the page they are trying to access. + + + + + + + 1.525in + + + + + + + true + true + + + + + ="Report Local Time: " & Globals!ExecutionTime + + + + + + + 0.375in + 0.125in + 0.25in + 3in + 1 + + + + Embedded + sql_logo + Fit + 8.375in + 0.5in + 1.75in + 2 + + + + + + + true + true + + + + + ="Dashboard Version: " + Parameters!version_string.Value + + + + + + + 7in + 0.25in + 3.125in + 1 + + + + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + 3 + + + + + + + + true + true + + + + + # of Waiters + + + + + + + 20 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Wait Time (ms) + + + + + + + 19 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + % Wait Time + + + + + + + 18 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + + + + + + + + + + + + 0.25in + + + + + true + + true + + true + + + + + =Fields!wait_category.Value + + + + + + 2pt + 2pt + 2pt + 2pt + + + 3 + + + + + + + + true + true + + + + + =Count(Fields!request_id.Value) + + + + + + + num_waits + 15 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Sum(Fields!wait_time.Value) + + + + + + + wait_time + 14 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =FormatPercent(IIf(Sum(Fields!wait_time.Value, "REQUESTS_WAITS") > 0, Sum(Fields!wait_time.Value) / Sum(Fields!wait_time.Value, "REQUESTS_WAITS"), 1), 2) + + + + + + + 13 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + + + + + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + 0.375in + + + + + true + true + + + + + + + + + + + + + + + + true + true + + + + + Session ID: Request ID + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Wait Type + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Wait Time (ms) + + + + + + + 8 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Wait Resource + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + 2 + + + + + + + true + true + + + + + Query Text + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + 0.25in + + + + + true + true + + + + + + + + + + + + + + + + true + true + + + + + =Fields!session_id.Value & IIf(Fields!request_id.Value is Nothing, "", ":" & Fields!request_id.Value) + + + + + + + session_id + + + + + session_details + + + =Parameters!Server.Value + + + =Fields!session_id.Value + + + =Parameters!version_string.Value + + + + + + + 4 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!wait_type.Value + + + + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!wait_time.Value + + + + + + + 2 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!wait_resource.Value + + + + + + 2pt + 2pt + 2pt + 2pt + + + 2 + + + + + + + true + true + + + + + =Fields!query_text.Value + + + + + + 2pt + 2pt + 2pt + 2pt + + + true + + + + + + + + + + + + + + + + + + + + + After + true + + + + + =Fields!wait_category.Value + + + + + =sum(Fields!wait_time.Value) + Descending + + + + + After + true + + + + wait_category + + After + true + + + + Detail + + + + =Fields!wait_time.Value + Descending + + + + + + wait_category + + + + Detail_Collection + Output + true + + + + + + There are currently no requests waiting on a resource. + REQUESTS_WAITS + 3.375in + 0.125in + 1.125in + 13.75in + + + true + -90 + + + + 6pt + + 0.75pt + + + + Wait Category + + + False + 1 + + False + + 0.75pt + + + NaN + + + False + + 0.75pt + + + NaN + -1 + + + True + + 0.75pt + + + NaN + + + None + + NaN + NaN + true + true + false + + + + + + + NaN + Opposite + NaN + NaN + + + 0.75pt + + + + Wait Time (ms) + + + True + + True + + 0.75pt + + + NaN + + + False + + 0.75pt + + + NaN + + + True + + 0.75pt + + + NaN + + + None + + true + 0 + true + true + false + + + + + + + NaN + Opposite + NaN + NaN + + + 0.75pt + + LightGrey + + + + + + true + + 0.75pt + + + RightCenter + Column + + + + + true + + + + + Cumulative Wait Time By Type + + + + + + + + No Data Available + + + REQUESTS_WAITS + 3.125in + 7.375in + + + __Upgraded2005__ + __Upgraded2005__ + + + + + + ContentsOnly + 0.125in + 0.125in + 3.125in + 7.75in + 1 + + =IIf(Count(Fields!session_id.Value, "REQUESTS_WAITS") = 0, true, false) + + + + + + + + true + true + + + + + ="Report Local Time: " & Globals!ExecutionTime + + + + + + + 0.375in + 0.125in + 0.25in + 3in + 1 + + + + Embedded + sql_logo + Fit + 7.625in + 0.5in + 1.75in + 2 + + + + + + + true + true + + + + + ="Dashboard Version: " + Parameters!version_string.Value + + + + + + + 6.125in + 0.25in + 3.125in + 1 + + + + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + + =Fields!waiting_requests_count.Value + + true + + + + + # of Waits + + + + + + + textbox3 + 6 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + + =Fields!wait_time_ms.Value + + true + + + + + Wait Time (ms) + + + + + + + textbox4 + 5 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + + =Fields!max_wait_time_ms.Value + + true + + + + + Max Wait Time (ms) + + + + + + + textbox5 + 4 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + 0.2in + + + + + true + true + + + + + =Fields!latch_class.Value + + + + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!waiting_requests_count.Value + + + + + + + waiting_requests_count + 2 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!wait_time_ms.Value + + + + + + + wait_time_ms + 1 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!max_wait_time_ms.Value + + + + + + + max_wait_time_ms + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + + + + + + + + + + + + + true + After + true + + + + Detail + + + + =Fields!wait_time_ms.Value + Descending + + + + + + Detail_Collection + Output + true + + + + LATCH_STATS + 1.375in + 0.125in + 0.4in + 9.375in + + + + + + + 1in + + + 1.75in + + + 1.375in + + + 2.375in + + + 5.5in + + + + + 0.375in + + + + + true + true + + + + + Session ID: Request ID + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Wait Type + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Wait Time (ms) + + + + + + + textbox12 + 7 + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Wait Resource + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + Query Text + + + + + + Gainsboro + 2pt + 2pt + 2pt + 2pt + + + + + + + + 0.2in + + + + + true + true + + + + + =Fields!session_id.Value & IIf(Fields!request_id.Value is Nothing, "", ":" & Fields!request_id.Value) + + + + + + + session_id + + + + + session_details + + + =Parameters!Server.Value + + + =Fields!session_id.Value + + + =Parameters!version_string.Value + + + + + + + 4 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!wait_type.Value + + + + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!wait_time.Value + + + + + + + wait_time + 2 + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!wait_resource.Value + + + + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + true + true + + + + + =Fields!query_text.Value + + + + + + 2pt + 2pt + 2pt + 2pt + + + true + + + + + + + + + + + + + + + + + + + true + After + true + + + + Detail + + + + + Detail_Collection + Output + true + + + + There are currently no requests waiting on a non-buffer latch. + REQUESTS_WITH_LATCH_WAITS + 0.375in + 0.125in + 0.575in + 12in + 1 + + + + true + true + + + + + This table shows all current requests with a non-buffer latch wait. + + + + + + + true + true + + + + + The following table shows aggregate wait statistics for each latch class. This data is cumulative since server startup or since performance statistics were last reset. + + + + + + + 1.775in + + + + + + + true + true + + + + + ="Report Local Time: " & Globals!ExecutionTime + + + + + + + 0.375in + 0.125in + 0.25in + 3in + 1 + + + + Embedded + sql_logo + Fit + 9.125in + 0.5in + 1.75in + 2 + + + + + + + true + true + + + + + ="Dashboard Version: " + Parameters!version_string.Value + + + + + + + 7.875in + 0.25in + 3.125in + 1 + + + + + + + + + 2pt + 2pt + 2pt + 2pt + + + + 2.25in + + + + + 6in + + + 0.45in + true + true + + + true + true + + + + + =Globals!ExecutionTime + + + + ExecutionTime + 0.2in + 4in + 0.25in + 2in + + + 2pt + 2pt + 2pt + 2pt + + + + + + + + 1in + 1in + 1in + 1in +