MSTeamsCQD.psm1
# (c)2018 Microsoft Corporation. All rights reserved. This document is provided "as-is." Information and views expressed in this document, # including URL and other Internet Web site references, may change without notice. You bear the risk of using it. # This document does not provide you with any legal rights to any intellectual property in any Microsoft product. # You may copy and use this document for your internal, reference purposes. You may modify this document for your internal purposes # ################################################ # CQD POWERSHELL ############################## # JUSTW/SETHHA # # QUERY CQD DIRECT FROM POWERSHELL AND EXPORT # # # # Modified by # Luca Sain ################################################ # Date: 30/03/2022 # Features added # added UNION filter to perform more advanced queries with AND/OR statements [CmdletBinding()] param( # Non Dynamic params go here # ) ############################### # BEGIN JWT DECODE FUNCTIONS # ############################### function Convert-64String ([string]$datai) { $datai = $datai.Replace('-', '+').Replace('_', '/') switch ($datai.Length % 4) { 0 { break } 2 { $datai += '==' } 3 { $datai += '=' } default { throw New-Object ArgumentException ('datai') } } return [System.Convert]::FromBase64String($datai) } # function Decode-JWT ([string]$rawToken) { $Tokenparts = $rawToken.Split('.'); $headers = [System.Text.Encoding]::UTF8.GetString((Convert-64String $Tokenparts[0])) $claims = [System.Text.Encoding]::UTF8.GetString((Convert-64String $Tokenparts[1])) $signature = (Convert-64String $Tokenparts[2]) $customObject = [pscustomobject]@{ headers = ($headers | ConvertFrom-Json) claims = ($claims | ConvertFrom-Json) signature = $signature } #Write-Verbose -Message ("JWT`r`n.headers: {0}`r`n.claims: {1}`r`n.signature: {2}`r`n" -f $headers,$claims,[System.BitConverter]::ToString($signature)) return $customObject } # function Get-JWTData { [CmdletBinding()] param ( # Param1 help description [Parameter(Mandatory = $true)] [string]$Token, [switch]$Recurse ) if ($Recurse) { $decoded = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($Token)) $DecodedJwt = Decode-JWT -rawToken $decoded } else { $DecodedJwt = Decode-JWT -rawToken $Token } #Write-Host("Token Values") -ForegroundColor Green #Write-Host ($DecodedJwt | Select headers,claims | ConvertTo-Json) return $DecodedJwt } ############################# # END JWT DECODE FUNCTIONS # ############################# ############################### # BEGIN AAD AUTH FUNCTIONS # ############################### ####Function to Get the JWT Token VIA OAuth function Get-CQDToken ([string]$client_id) { if ($CQDVer -eq "V2") { $CQDUri = "https://cqd.lync.com/spd/" $V2Token = $true } else { $CQDUri = "https://cqd.teams.microsoft.com/spd/" $V3Token = $true } Add-Type -AssemblyName System.Web $resourceUrl = $WebResource $redirectUrl = $CQDUri $nonce = [guid]::NewGuid().GUID $url = "https://login.microsoftonline.com/common/oauth2/authorize?response_type=token&redirect_uri=" + [System.Web.HttpUtility]::UrlEncode($redirectUrl) + "&client_id=$client_id" + "&prompt=login" + "&nonce=$nonce" + "&resource=" + [System.Web.HttpUtility]::UrlEncode($WebResource) Add-Type -AssemblyName System.Windows.Forms $form = New-Object -TypeName System.Windows.Forms.Form -Property @{ Width = 440; Height = 640 } $web = New-Object -TypeName System.Windows.Forms.WebBrowser -Property @{ Width = 420; Height = 600; Url = ($url) } $DocComp = { $Global:uri = $web.Url.AbsoluteUri #Write-Host $Global:uri if ($Global:Uri -match "error=[^&]*|access_token=[^&]*") { $form.Close() } } $web.ScriptErrorsSuppressed = $true $web.Add_DocumentCompleted($DocComp) $form.Controls.Add($web) $form.Add_Shown({ $form.Activate() }) $form.ShowDialog() | Out-Null $Script:TokenLifeTime = [Web.HttpUtility]::ParseQueryString(($web.Url -replace '^.*?(expires_in.+)$', '$1'))['expires_in'] #Write-Host $TokenLifeTime $Script:Token = [Web.HttpUtility]::ParseQueryString(($web.Url -replace '^.*?(access_token.+)$', '$1'))['access_token'] # Write-Host $Token return ('Bearer {0}' -f $Script:Token) } #Function to connect to CQD Via AzureAD function Connect-CqdOnline { if ($CQDVer -eq "V2") { $UriVar = "https://repository.cqd.lync.com/clientconfiguration" } else { $UriVar = "https://cqd.teams.microsoft.com/repository/clientconfiguration" } $ClientConfigUri = $UriVar $Script:Configuration = Invoke-RestMethod -Uri $ClientConfigUri -Method Get -SessionVariable WebSession -UserAgent "CQDPowerShell V2.0" $Script:WebResource = $Configuration.AuthLoginResource $Script:AADBearerToken = Get-CQDToken $Configuration.AuthWebAppClientId $Script:WebSession = $WebSession $Script:RepositoryApiBaseUrl = $Script:Configuration.RepositoryApiBaseUrl $WebSession.headers.Add('Authorization', $AADBearerToken) $ProvisioningStatus = Invoke-RestMethod -Uri ('{0}tenant/provision' -f $RepositoryApiBaseUrl) -Method Get -WebSession $Script:WebSession -UserAgent "CQDPowerShell V2.0" if ($ProvisioningStatus.Status -ne 'Provisioned') { throw ('CQD Not Provisioned for TenantId {0}. Stopping...' -f $ProvisioningStatus.TenantId) } $Script:DataServiceBaseUrl = Invoke-RestMethod -Uri ('{0}tenant/dataservice' -f $Script:RepositoryApiBaseUrl) -WebSession $Script:WebSession -Method Get -UserAgent "CQDPowerShell V2.0" } Export-ModuleMember -Function Connect-CqdOnline #Function to validate if token exists or is valid and unexpired function CheckToken { #Check to see if Token already exists if ($Script:AADBearerToken -eq $null) { Connect-CqdOnline } else { #Check Token $JWTDecoded = Get-JWTData $Script:Token $JWTI = $JWTDecoded.claims.iat $JWTE = $JWTDecoded.claims.exp $NowTimeUTC = Get-Date $origin = New-Object -Type DateTime -ArgumentList 1970, 1, 1, 0, 0, 0, 0 $JWTIssuedTime = $origin.AddSeconds($JWTI) $JWTExpireTime = $origin.AddSeconds($JWTE) #Write-Host $JWTIssuedTime $JWTExpireTime if ($CQDVer -eq "V2") { if ($JWTDecoded.claims.aud -eq "https://cqd.lync.com") { if ($NowTimeUTC.ToUniversalTime() -gt $JWTExpireTime) { Write-Host "Token Expired" Connect-CqdOnline } } else { write-host "Wrong Token, Expecting V2" Connect-CqdOnline } } elseif ($CQDVer -eq "V3") { if ($JWTDecoded.claims.aud -eq "https://cqd.teams.microsoft.com") { if ($NowTimeUTC.ToUniversalTime() -gt $JWTExpireTime) { Write-Host "Token Expired" Connect-CqdOnline } } else { write-host "Wrong Token, Expecting V3" Connect-CqdOnline } } } } ############################### # END AAD AUTH FUNCTIONS # ############################### #Function to get Array list of Dimensions available in CQD Cube function Get-CQDDimensions { $DimensionsCheck = GetParams "Dimensions" return $DimensionsCheck } Export-ModuleMember -Function Get-CQDDimensions function Get-CQDMeasures { $MeasuresCheck = GetParams "Measures" return $MeasuresCheck } Export-ModuleMember -Function Get-CQDMeasures ##Generate Dynamic List of CQD Measures and Dimensions function GetParams ([string]$ParamType) { CheckToken $CubeRequest = Invoke-WebRequest -Uri ('{0}CubeStructure' -f $DataServiceBaseUrl) -WebSession $WebSession -Method Get -UserAgent "CQDPowerShell V2.0" $List = ConvertFrom-Json $CubeRequest $D = $List.Dimensions $Meas = $List.Measurements $Dimensions = @() $Measures = @() foreach ($i in $D) { $Dimensions += $i.Category $Dimensions += $i.Attributes } foreach ($a in $Meas) { $Measures += $a.Category $Measures += $a.Attributes } if ($ParamType -eq "Dimensions") { $DimensionName = $Dimensions.DataModelName $Di = @() foreach ($O in $DimensionName) { $O = $O.Replace("[", "") $O = $O.Replace("]", "") $Di += $O } return $Di } elseif ($ParamType -eq "Measures") { $MeasureName = $Measures.DataModelName $Mi = @() foreach ($M in $MeasureName) { $M = $M.Replace("[", "") $M = $M.Replace("]", "") $Mi += $M } return $Mi } } #Function for handling start-end date function ProcessDateRange { $TS = New-TimeSpan $StartDate $EndDate if ($TS.Days -lt 0) { $DateErrorStartEnd = 'Error in Timerange. Confirm StartDate is BEFORE EndDate' Write-Host $DateErrorStartEnd throw $DateErrorStartEnd } $i = $TS.Days if ($StartDate -eq $EndDate) { $DateList = @($EndDate) } else { $DateList = @($EndDate) do { $DateList += $EndDate.AddDays(-$i) $i -- } while ($i -ne 0) } foreach ($D in $DateList) { $DateF += "[" + $D.ToString('yyyy-MM-dd') + "]," } return $DateF } #Function to obtain days of week function GetDaysofWeek { foreach ($DofWT in $Weekdays) { $DofW += "[" + $DofWT + "]," } return $DofW } function BuildWeekFilter { param( [Parameter()] [array]$TimeInput ) # Create list containing "start of weeks'" (Sun) for last 6 month $endDay = Get-Date $startDay = (Get-Date).AddMonths(-6) $cqdLastSixMonthWeeks = @() for ($i = $startDay; $i -lt $endDay; $i = $i.AddDays(1)) { if (($i.DayOfWeek).ToString() -eq "Sunday") { $cqdLastSixMonthWeeks += $i.ToString("yyyy-MM-dd") } } # Initialize variable that will keep final list of "start of weeks'" $cqdWeeks = @() # There are two cases: # 1. Month is provided as an input e.g. "09" for Sep. Must be in this format. length of 2 # 2. Start of week is provided as an input e.g. "2017-09-18". Must be in this format. length of 10 $TimeInput | ForEach-Object { if ($_.Length -eq 2) { $tempMonth = $_ $cqdLastSixMonthWeeks | ForEach-Object { if ($_ -like "*-$($tempMonth)-*") { $cqdWeeks += $_ } } } elseif ($_.Length -eq 10) { $cqdWeeks += $_ } } # Create hash table representing CQD Week filter $cqdFilterWeek = @{} $Caption = "" $Value = "" $cqdWeeks | ForEach-Object { $Caption += "$($_) | " } $cqdWeeks | ForEach-Object { $Value += "[$($_)]," } $cqdFilterWeek.Add("DataModelName", "[AllStreams].[Week]") $cqdFilterWeek.Add("Caption", $Caption) $cqdFilterWeek.Add("Value", $Value) $cqdFilterWeek.Add("Operand", 0) $cqdFilterWeek.Add("UnionGroup", "") # Return Json object representing CQD Week filter return ($cqdFilterWeek | ConvertTo-Json) } #Function for creating UA Category Filter function BuildUACategoryFilter { param( [Parameter()] [array]$UACategory ) # Create hash table representing CQD UA Category filter $cqdFilterUACategory = @{} $Caption = "" $Value = "" $UACategory | ForEach-Object { $Caption += "$($_) | " } $Caption | ForEach-Object { $Length = $Caption.Trim().Length $Caption = $Caption.Trim().Substring(0, $Length - 1) $Caption = $Caption.Trim() } $UACategory | ForEach-Object { $Value += "[$($_)]," } $Value | ForEach-Object { $Length = $Value.Trim().Length $Value = $Value.Trim().Substring(0, $Length - 1) $Value = $Value.Trim() } $cqdFilterUACategory.Add("DataModelName", "[AllStreams].[Second User Agent Category]") $cqdFilterUACategory.Add("Caption", $Caption) $cqdFilterUACategory.Add("Value", $Value) $cqdFilterUACategory.Add("Operand", 0) $cqdFilterUACategory.Add("UnionGroup", "") # Return Json object representing CQD UA Category filter return ($cqdFilterUACategory | ConvertTo-Json) } #Function for creating Media Type Filter function BuildMediaTypeFilter { param( [Parameter()] [array]$MediaType ) # Create hash table representing CQD Media Type filter $cqdFilterMediaType = @{} $Caption = "" $Value = "" $MediaType | ForEach-Object { $Caption += "$($_) | " } $Caption | ForEach-Object { $Length = $Caption.Trim().Length $Caption = $Caption.Trim().Substring(0, $Length - 1) $Caption = $Caption.Trim() } $MediaType | ForEach-Object { $Value += "[$($_)]," } $Value | ForEach-Object { $Length = $Value.Trim().Length $Value = $Value.Trim().Substring(0, $Length - 1) $Value = $Value.Trim() } $cqdFilterMediaType.Add("DataModelName", "[AllStreams].[Media Type]") $cqdFilterMediaType.Add("Caption", $Caption) $cqdFilterMediaType.Add("Value", $Value) $cqdFilterMediaType.Add("Operand", 0) $cqdFilterMediaType.Add("UnionGroup", "") # Return Json object representing CQD Media Type filter return ($cqdFilterMediaType | ConvertTo-Json) } #Function for Server Pair Filter function BuildIsServerPairFilter { param( [Parameter()] [array]$IsServerPair ) # Create hash table representing CQD Is Server Pair filter $cqdFilterIsServerPair = @{} $Caption = "" $Value = "" $IsServerPair | ForEach-Object { $Caption += "$($_) | " } $Caption | ForEach-Object { $Length = $Caption.Trim().Length $Caption = $Caption.Trim().Substring(0, $Length - 1) $Caption = $Caption.Trim() } $IsServerPair | ForEach-Object { $Value += "[$($_)]," } $Value | ForEach-Object { $Length = $Value.Trim().Length $Value = $Value.Trim().Substring(0, $Length - 1) $Value = $Value.Trim() } $cqdFilterIsServerPair.Add("DataModelName", "[AllStreams].[Is Server Pair]") $cqdFilterIsServerPair.Add("Caption", $Caption) $cqdFilterIsServerPair.Add("Value", $Value) $cqdFilterIsServerPair.Add("Operand", 0) $cqdFilterIsServerPair.Add("UnionGroup", "") # Return Json object representing CQD Is Server Pair filter return ($cqdFilterIsServerPair | ConvertTo-Json) } #Function for Transport Filter (TCP,UDP) function BuildTransportFilter { param( [Parameter()] [array]$Transport ) # Create hash table representing CQD transport filter $cqdFilterTransport = @{} $Caption = "" $Value = "" $Transport | ForEach-Object { $Caption += "$($_) | " } $Caption | ForEach-Object { $Length = $Caption.Trim().Length $Caption = $Caption.Trim().Substring(0, $Length - 1) $Caption = $Caption.Trim() } $Transport | ForEach-Object { $Value += "[$($_)]," } $Value | ForEach-Object { $Length = $Value.Trim().Length $Value = $Value.Trim().Substring(0, $Length - 1) $Value = $Value.Trim() } $cqdFilterTransport.Add("DataModelName", "[AllStreams].[Transport]") $cqdFilterTransport.Add("Caption", $Caption) $cqdFilterTransport.Add("Value", $Value) $cqdFilterTransport.Add("Operand", 0) $cqdFilterTransport.Add("UnionGroup", "") # Return Json object representing CQD transport filter return ($cqdFilterTransport | ConvertTo-Json) } #Function for building all Filters function FilterCreation { #Build MonthYear Array From Input foreach ($MonthYearI in $MonthYear) { $MonthYearParams += "[" + $MonthYearI + "]," } $Filters = New-Object system.Data.DataTable "Filters" $FilterName = New-Object system.Data.DataColumn FilterName, ([string]) $FilterValue = New-Object system.Data.DataColumn FilterValue, ([string]) $FilterCaption = New-Object system.Data.DataColumn FilterCaption, ([string]) $FilterOperand = New-Object system.Data.DataColumn FilterOperand, ([string]) $FilterUnionGroup = New-Object system.Data.DataColumn FilterUnionGroup, ([string]) #Add the Columns $Filters.columns.Add($FilterName) $Filters.columns.Add($FilterValue) $Filters.columns.Add($FilterCaption) $Filters.columns.Add($FilterOperand) $Filters.columns.Add($FilterUnionGroup) if ($Weekdays -ne $null) { $row = $Filters.NewRow() $row.FilterName = "AllStreams.Day Of Week" $row.FilterValue = "$DaysofWeekList" $row.FilterCaption = "NA" $row.FilterOperand = "0" $row.FilterUnionGroup = "" $Filters.Rows.Add($row) } if ($MonthYear -ne $null) { #Create a row $row = $Filters.NewRow() $row.FilterName = "AllStreams.Month Year" $row.FilterValue = "$MonthYearParams" $row.FilterCaption = "NA" $row.FilterOperand = "0" $row.FilterUnionGroup = "" $Filters.Rows.Add($row) } if ($StartDate -ne $null -and $EndDate -ne $null) { $EndDateV = ProcessDateRange #Create a row $row = $Filters.NewRow() $row.FilterName = "AllStreams.Date" $row.FilterValue = "$EndDateV" $row.FilterCaption = "NA" $row.FilterOperand = "0" $row.FilterUnionGroup = "" $Filters.Rows.Add($row) } if ($Week -ne $null) { $WeekV = (BuildWeekFilter $Week) | ConvertFrom-Json $row = $Filters.NewRow() $row.FilterName = "AllStreams.Week" $row.FilterValue = $WeekV.Value $row.FilterCaption = "NA" $row.FilterOperand = $WeekV.Operand $row.FilterUnionGroup = $WeekV.UnionGroup $Filters.Rows.Add($row) } if ($UACategory -ne $null) { $UACategoryV = (BuildUACategoryFilter $UACategory) | ConvertFrom-Json $row = $Filters.NewRow() $row.FilterName = "AllStreams.Second User Agent Category" $row.FilterValue = $UACategoryV.Value $row.FilterCaption = "NA" $row.FilterOperand = $UACategoryV.Operand $row.FilterUnionGroup = $UACategoryV.UnionGroup $Filters.Rows.Add($row) } if ($MediaType -ne $null) { $MediaTypeV = (BuildMediaTypeFilter $MediaType) | ConvertFrom-Json $row = $Filters.NewRow() $row.FilterName = "AllStreams.Media Type" $row.FilterValue = $MediaTypeV.Value $row.FilterCaption = "NA" $row.FilterOperand = $MediaTypeV.Operand $row.FilterUnionGroup = $MediaTypeV.UnionGroup $Filters.Rows.Add($row) } if ($IsServerPair -ne $null) { $IsServerPairV = (BuildIsServerPairFilter $IsServerPair) | ConvertFrom-Json $row = $Filters.NewRow() $row.FilterName = "AllStreams.Is Server Pair" $row.FilterValue = $IsServerPairV.Value $row.FilterCaption = "NA" $row.FilterOperand = $IsServerPairV.Operand $row.FilterUnionGroup = $IsServerPairV.UnionGroup $Filters.Rows.Add($row) } if ($Transport -ne $null) { $TransportV = (BuildTransportFilter $Transport) | ConvertFrom-Json $row = $Filters.NewRow() $row.FilterName = "AllStreams.Transport" $row.FilterValue = $TransportV.Value $row.FilterCaption = "NA" $row.FilterOperand = $TransportV.Operand $row.FilterUnionGroup = $TransportV.UnionGroup $Filters.Rows.Add($row) } if ($null -ne $CustomFilter) { foreach ($F in $CustomFilter) { $row = $Filters.NewRow() $row.FilterName = $F.FName $row.FilterValue = $F.FValue $row.FilterCaption = "" $row.FilterOperand = $F.Op $row.FilterUnionGroup = $F.FUnionGroup $Filters.Rows.Add($row) } } return $Filters } function Get-CQDData { [CmdletBinding()] param( [Parameter(Mandatory = $false, Position = 3)] [array]$Transport = $null, [Parameter(Mandatory = $false, Position = 4)] [string]$OutPutFilePath = $null, [Parameter(Mandatory = $false, Position = 5)] [nullable[datetime]]$StartDate = $null, [Parameter(Mandatory = $false, Position = 6)] [nullable[datetime]]$EndDate = $null, [Parameter(Mandatory = $false, Position = 7)] [array]$MonthYear = $null, [Parameter(Mandatory = $false, Position = 8)] [array]$Weekdays = $null, [Parameter(Mandatory = $True, Position = 9)] [ValidateSet("CSV", "DataTable")] [string]$OutPutType, [Parameter(Mandatory = $false, Position = 10)] [array]$Week = $null, [Parameter(Mandatory = $false, Position = 11)] [array]$UACategory = $null, [Parameter(Mandatory = $false, Position = 12)] [array]$MediaType = $null, [Parameter(Mandatory = $false, Position = 13)] [ValidateSet("Client : Client", "Client : Server", "Server : Server")] [array]$IsServerPair = $null, [Parameter(Mandatory = $false, Position = 14)] [bool]$ShowQuery = $false, [Parameter(Mandatory = $false, Position = 15)] [switch] $OverWriteOutput, [Parameter(Mandatory = $false, Position = 16)] [pscustomobject] $CustomFilter, [Parameter(Mandatory = $false, Position = 17)] [switch] $LargeQuery, [Parameter(Mandatory = $false, Position = 18)] [ValidateSet("V2", "V3")] [string]$CQDVer = "V3" ) dynamicparam { # Set the dynamic parameters' name for Dimensions and Measures $DimensionParameter = 'Dimensions' $MeasuresParameter = 'Measures' # Create the dictionary $RuntimeParamDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary # Create the collection of attributes $DimensionAttributeCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute] $AttributeCollectionMeasures = New-Object System.Collections.ObjectModel.Collection[System.Attribute] # Create and set the parameters' attributes $DimensionParameterAttribute = New-Object System.Management.Automation.ParameterAttribute $DimensionParameterAttribute.Mandatory = $true $DimensionParameterAttribute.Position = 1 $MeasuresParameterAttribute = New-Object System.Management.Automation.ParameterAttribute $MeasuresParameterAttribute.Mandatory = $true $MeasuresParameterAttribute.Position = 2 # Add the attributes to the attributes collection $DimensionAttributeCollection.Add($DimensionParameterAttribute) $AttributeCollectionMeasures.Add($MeasuresParameterAttribute) # Generate and set the ValidateSet $DimensionarrSet = GetParams "Dimensions" $MeasuresarrSet = GetParams "Measures" $DimensionsValidateSetAttribute = New-Object System.Management.Automation.ValidateSetAttribute ($DimensionarrSet) $MeasuresValidateSetAttribute = New-Object System.Management.Automation.ValidateSetAttribute ($MeasuresarrSet) # Add the ValidateSet to the attributes collection $DimensionAttributeCollection.Add($DimensionsValidateSetAttribute) $AttributeCollectionMeasures.Add($MeasuresValidateSetAttribute) # Create and return the dynamic parameter $DimensionsRuntimeParameter = New-Object System.Management.Automation.RuntimeDefinedParameter ($DimensionParameter, [array], $DimensionAttributeCollection) $MeasuresRuntimeParameter = New-Object System.Management.Automation.RuntimeDefinedParameter ($MeasuresParameter, [array], $AttributeCollectionMeasures) $RuntimeParamDictionary.Add($DimensionParameter, $DimensionsRuntimeParameter) $RuntimeParamDictionary.Add($MeasuresParameter, $MeasuresRuntimeParameter) return $RuntimeParamDictionary } begin { # Bind the parameter to a friendly variable $Dims = $PsBoundParameters[$DimensionParameter] $Measures = $PsBoundParameters[$MeasuresParameter] } process { try { Write-Host "Working...." #Check for Null CSV Path if CSV Chosen output if ($OutPutType -eq "CSV" -and $OutPutFilePath -eq "") { while ($OutPutFilePath -eq "" -or $OutPutFilePath -eq $null) { Write-Host "You must specify an output path when using CSV output type" Write-Host "Enter CSV Output Path:" -NoNewline $OutputFilePath = Read-Host } if ($OutputFilePath -eq $null) { Exit-PSSession } } $DimFixed = @() $DimSimple = @() $MeasureFixed = @() $MeasureSimple = @() ###Fix Dimension Brackets foreach ($DimShort in $Dims) { $Dim1 = $DimShort.Split(".") $DimSimple += $Dim1[1].ToString() $Dim1[0] = "[" + $Dim1[0] + "]" $Dim1[1] = "[" + $Dim1[1] + "]" $DimFixed += $Dim1[0] + "." + $Dim1[1] } foreach ($MeasureShort in $Measures) { $Measure1 = $MeasureShort.Split(".") $MeasureSimple += $Measure1[1].ToString() $Measure1[0] = "[" + $Measure1[0] + "]" $Measure1[1] = "[" + $Measure1[1] + "]" $MeasureFixed += $Measure1[0] + "." + $Measure1[1] } $Dims = $DimFixed $Measures = $MeasureFixed if ($Weekdays -ne $null) { $DaysofWeekList = GetDaysofWeek } else { $Weekdays = $null } $Filters = FilterCreation ##Dimension and Measure Arrays## $DimensionsArray = $Dims $MeasurementsArray = $Measures ##Create Results Table## $DataResults = New-Object system.Data.DataTable "DataResults" foreach ($DimensionVar in $DimSimple) { $Column = New-Object system.Data.DataColumn $DimensionVar, ([string]) $DataResults.columns.Add($Column) } foreach ($MeasureVar in $MeasureSimple) { $Column = New-Object system.Data.DataColumn $MeasureVar, ([string]) $DataResults.columns.Add($Column) } $FilterRowArray = @() $DimensionRowArray = @() $MeasureRowArray = @() $AllArray = $DimSimple + $MeasureSimple ##Build Query Structure## #Start/End Query $StartQuery = "{" $EndQuery = "}" #Filter Sections $StartFilters = @' "Filters":[ '@ #Dimension Sections $StartDimensions = @' "Dimensions":[ '@ #Measure Sections $StartMeasures = @' "Measurements":[ '@ ###############Loop Arrays of Each Type foreach ($Filter in $Filters) { $FLN = $Filter.FilterName $FCN = $Filter.FilterCaption $FVE = $Filter.FilterValue $FOP = $Filter.FilterOperand $UNGP = $Filter.FilterUnionGroup $FLNSplit = $FLN.Split(".") $FLNName = "[" + $FLNSplit[0] + "].[" + $FLNSplit[1] + "]" $FilterRowArray += @" {"DataModelName":"$FLNName","Caption":"$FCN","Value":"$FVE","Operand":$FOP,"UnionGroup":"$UNGP"}, "@ } $DimCount = $DimensionsArray.Count - 1 foreach ($DimensionLoop in $DimensionsArray) { $DimensionRowArray += @" {"DataModelName":"$DimensionLoop"}, "@ } $MeaCount = $MeasurementsArray.Count - 1 foreach ($MeasureLoop in $MeasurementsArray) { $MeasureRowArray += @" {"DataModelName":"$MeasureLoop"}, "@ } ########Build Dynamic Query if ($LargeQuery) { $Query = @" { $StartFilters $FilterRowArray ] , $StartDimensions $DimensionRowArray ] , $StartMeasures $MeasureRowArray ] , LimitResultRowsCount:200000 } "@ } else { $Query = @" { $StartFilters $FilterRowArray ] , $StartDimensions $DimensionRowArray ] , $StartMeasures $MeasureRowArray ] } "@ } #Check to see if ShowQuery is true. Used to show JSON query for debugging queries. if ($ShowQuery -eq $true) { Write-Host "JSON Query:" Write-Host $Query } CheckToken $verb = "POST" $content = $Query $headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" $IRM = Invoke-RestMethod -Uri ('{0}RunQuery' -f $DataServiceBaseUrl) -WebSession $WebSession -Method Post -Body $Query -ContentType 'application/json' -UserAgent "CQDPowerShell V2.0" -Headers $headers $IRM2 = ConvertTo-Json -InputObject $IRM $JsonGarb = ConvertFrom-Json -InputObject $IRM2 $QueryExecutionTime = $JsonGarb.ExecutionTime $ErrorType = $JsonGarb.ErrorType $ResultList = $JsonGarb.DataResult if ($ErrorType -ne 0) { Write-Host "Error Querying CQD with Error " $ErrorType if ($ErrorType -eq 6) { Write-Host "Invalid Query. Check the query and try again." } if ($ErrorType -eq 5) { Write-Host "Query scope too large. Reduce Dimensions, Measures and Timespan" } if ($ErrorType -eq 3) { Write-Host "Cube currently unavailable. Try again later." } } foreach ($ResultItem in $ResultList) { if (!$ResultItem) { #if(1 -eq 2) Write-Host "Warning: Result is Null:" $ResultItem } else { $ItemCount = $ResultItem.Count $FieldCount = $DimensionsArray.Count + $MeasurementsArray.Count #Create a row $Resultrow = $DataResults.NewRow() $i = 0 do { $Resultrow.($AllArray[$i]) = $ResultItem.Item($i) $i++ } while ($i -lt $AllArray.Count) $DataResults.Rows.Add($Resultrow) } } if ($OutPutType -eq "CSV") { if ($OverWriteOutput -eq $true) { $DataResults | Export-Csv -Path $OutPutFilePath -notypeinformation } else { $DataResults | Export-Csv -Path $OutPutFilePath -notypeinformation -Append } } elseif ($OutPutType -eq "DataTable") { return $DataResults } Write-Host "Execution Complete. Query took " $QueryExecutionTime } catch [System.Net.WebException] { "Unable to complete query. Please reduce query scope or try again later." } catch [System.Management.Automation.ParameterBindingException] { "Error Binding" } catch { "Error: $Error" } } } Export-ModuleMember -Function Get-CQDData |