CQDPowerShell.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 # # # ################################################ [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 = "" $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 # SIG # Begin signature block # MIIjqQYJKoZIhvcNAQcCoIIjmjCCI5YCAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCArDUlAExVT+b92 # 6l8Q72eIMeqwn9nULtn7aJ1sPxqCjqCCDYUwggYDMIID66ADAgECAhMzAAABiK9S # 1rmSbej5AAAAAAGIMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD # VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p # bmcgUENBIDIwMTEwHhcNMjAwMzA0MTgzOTQ4WhcNMjEwMzAzMTgzOTQ4WjB0MQsw # CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u # ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB # AQCSCNryE+Cewy2m4t/a74wZ7C9YTwv1PyC4BvM/kSWPNs8n0RTe+FvYfU+E9uf0 # t7nYlAzHjK+plif2BhD+NgdhIUQ8sVwWO39tjvQRHjP2//vSvIfmmkRoML1Ihnjs # 9kQiZQzYRDYYRp9xSQYmRwQjk5hl8/U7RgOiQDitVHaU7BT1MI92lfZRuIIDDYBd # vXtbclYJMVOwqZtv0O9zQCret6R+fRSGaDNfEEpcILL+D7RV3M4uaJE4Ta6KAOdv # V+MVaJp1YXFTZPKtpjHO6d9pHQPZiG7NdC6QbnRGmsa48uNQrb6AfmLKDI1Lp31W # MogTaX5tZf+CZT9PSuvjOCLNAgMBAAGjggGCMIIBfjAfBgNVHSUEGDAWBgorBgEE # AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUj9RJL9zNrPcL10RZdMQIXZN7MG8w # VAYDVR0RBE0wS6RJMEcxLTArBgNVBAsTJE1pY3Jvc29mdCBJcmVsYW5kIE9wZXJh # dGlvbnMgTGltaXRlZDEWMBQGA1UEBRMNMjMwMDEyKzQ1ODM4NjAfBgNVHSMEGDAW # gBRIbmTlUAXTgqoXNzcitW2oynUClTBUBgNVHR8ETTBLMEmgR6BFhkNodHRwOi8v # d3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NybC9NaWNDb2RTaWdQQ0EyMDExXzIw # MTEtMDctMDguY3JsMGEGCCsGAQUFBwEBBFUwUzBRBggrBgEFBQcwAoZFaHR0cDov # L3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9jZXJ0cy9NaWNDb2RTaWdQQ0EyMDEx # XzIwMTEtMDctMDguY3J0MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggIB # ACnXo8hjp7FeT+H6iQlV3CcGnkSbFvIpKYafgzYCFo3UHY1VHYJVb5jHEO8oG26Q # qBELmak6MTI+ra3WKMTGhE1sEIlowTcp4IAs8a5wpCh6Vf4Z/bAtIppP3p3gXk2X # 8UXTc+WxjQYsDkFiSzo/OBa5hkdW1g4EpO43l9mjToBdqEPtIXsZ7Hi1/6y4gK0P # mMiwG8LMpSn0n/oSHGjrUNBgHJPxgs63Slf58QGBznuXiRaXmfTUDdrvhRocdxIM # i8nXQwWACMiQzJSRzBP5S2wUq7nMAqjaTbeXhJqD2SFVHdUYlKruvtPSwbnqSRWT # GI8s4FEXt+TL3w5JnwVZmZkUFoioQDMMjFyaKurdJ6pnzbr1h6QW0R97fWc8xEIz # LIOiU2rjwWAtlQqFO8KNiykjYGyEf5LyAJKAO+rJd9fsYR+VBauIEQoYmjnUbTXM # SY2Lf5KMluWlDOGVh8q6XjmBccpaT+8tCfxpaVYPi1ncnwTwaPQvVq8RjWDRB7Pa # 8ruHgj2HJFi69+hcq7mWx5nTUtzzFa7RSZfE5a1a5AuBmGNRr7f8cNfa01+tiWjV # Kk1a+gJUBSP0sIxecFbVSXTZ7bqeal45XSDIisZBkWb+83TbXdTGMDSUFKTAdtC+ # r35GfsN8QVy59Hb5ZYzAXczhgRmk7NyE6jD0Ym5TKiW5MIIHejCCBWKgAwIBAgIK # YQ6Q0gAAAAAAAzANBgkqhkiG9w0BAQsFADCBiDELMAkGA1UEBhMCVVMxEzARBgNV # BAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jv # c29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJvb3QgQ2VydGlm # aWNhdGUgQXV0aG9yaXR5IDIwMTEwHhcNMTEwNzA4MjA1OTA5WhcNMjYwNzA4MjEw # OTA5WjB+MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UE # BxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSgwJgYD # VQQDEx9NaWNyb3NvZnQgQ29kZSBTaWduaW5nIFBDQSAyMDExMIICIjANBgkqhkiG # 9w0BAQEFAAOCAg8AMIICCgKCAgEAq/D6chAcLq3YbqqCEE00uvK2WCGfQhsqa+la # UKq4BjgaBEm6f8MMHt03a8YS2AvwOMKZBrDIOdUBFDFC04kNeWSHfpRgJGyvnkmc # 6Whe0t+bU7IKLMOv2akrrnoJr9eWWcpgGgXpZnboMlImEi/nqwhQz7NEt13YxC4D # dato88tt8zpcoRb0RrrgOGSsbmQ1eKagYw8t00CT+OPeBw3VXHmlSSnnDb6gE3e+ # lD3v++MrWhAfTVYoonpy4BI6t0le2O3tQ5GD2Xuye4Yb2T6xjF3oiU+EGvKhL1nk # kDstrjNYxbc+/jLTswM9sbKvkjh+0p2ALPVOVpEhNSXDOW5kf1O6nA+tGSOEy/S6 # A4aN91/w0FK/jJSHvMAhdCVfGCi2zCcoOCWYOUo2z3yxkq4cI6epZuxhH2rhKEmd # X4jiJV3TIUs+UsS1Vz8kA/DRelsv1SPjcF0PUUZ3s/gA4bysAoJf28AVs70b1FVL # 5zmhD+kjSbwYuER8ReTBw3J64HLnJN+/RpnF78IcV9uDjexNSTCnq47f7Fufr/zd # sGbiwZeBe+3W7UvnSSmnEyimp31ngOaKYnhfsi+E11ecXL93KCjx7W3DKI8sj0A3 # T8HhhUSJxAlMxdSlQy90lfdu+HggWCwTXWCVmj5PM4TasIgX3p5O9JawvEagbJjS # 4NaIjAsCAwEAAaOCAe0wggHpMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBRI # bmTlUAXTgqoXNzcitW2oynUClTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTAL # BgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBRyLToCMZBD # uRQFTuHqp8cx0SOJNDBaBgNVHR8EUzBRME+gTaBLhklodHRwOi8vY3JsLm1pY3Jv # c29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFf # MDNfMjIuY3JsMF4GCCsGAQUFBwEBBFIwUDBOBggrBgEFBQcwAoZCaHR0cDovL3d3 # dy5taWNyb3NvZnQuY29tL3BraS9jZXJ0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFf # MDNfMjIuY3J0MIGfBgNVHSAEgZcwgZQwgZEGCSsGAQQBgjcuAzCBgzA/BggrBgEF # BQcCARYzaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9kb2NzL3ByaW1h # cnljcHMuaHRtMEAGCCsGAQUFBwICMDQeMiAdAEwAZQBnAGEAbABfAHAAbwBsAGkA # YwB5AF8AcwB0AGEAdABlAG0AZQBuAHQALiAdMA0GCSqGSIb3DQEBCwUAA4ICAQBn # 8oalmOBUeRou09h0ZyKbC5YR4WOSmUKWfdJ5DJDBZV8uLD74w3LRbYP+vj/oCso7 # v0epo/Np22O/IjWll11lhJB9i0ZQVdgMknzSGksc8zxCi1LQsP1r4z4HLimb5j0b # pdS1HXeUOeLpZMlEPXh6I/MTfaaQdION9MsmAkYqwooQu6SpBQyb7Wj6aC6VoCo/ # KmtYSWMfCWluWpiW5IP0wI/zRive/DvQvTXvbiWu5a8n7dDd8w6vmSiXmE0OPQvy # CInWH8MyGOLwxS3OW560STkKxgrCxq2u5bLZ2xWIUUVYODJxJxp/sfQn+N4sOiBp # mLJZiWhub6e3dMNABQamASooPoI/E01mC8CzTfXhj38cbxV9Rad25UAqZaPDXVJi # hsMdYzaXht/a8/jyFqGaJ+HNpZfQ7l1jQeNbB5yHPgZ3BtEGsXUfFL5hYbXw3MYb # BL7fQccOKO7eZS/sl/ahXJbYANahRr1Z85elCUtIEJmAH9AAKcWxm6U/RXceNcbS # oqKfenoi+kiVH6v7RyOA9Z74v2u3S5fi63V4GuzqN5l5GEv/1rMjaHXmr/r8i+sL # gOppO6/8MO0ETI7f33VtY5E90Z1WTk+/gFcioXgRMiF670EKsT/7qMykXcGhiJtX # cVZOSEXAQsmbdlsKgEhr/Xmfwb1tbWrJUnMTDXpQzTGCFXowghV2AgEBMIGVMH4x # CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRt # b25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01p # Y3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMTECEzMAAAGIr1LWuZJt6PkAAAAA # AYgwDQYJYIZIAWUDBAIBBQCggbQwGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQw # HAYKKwYBBAGCNwIBCzEOMAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEILWF # XZrLrztKEc7IgKiWyWik43urg7Rh8XoFjY3h3yzjMEgGCisGAQQBgjcCAQwxOjA4 # oByAGgBDAFEARABQAG8AdwBlAHIAUwBoAGUAbABsoRiAFmh0dHBzOi8vbWljcm9z # b2Z0LmNvbSAwDQYJKoZIhvcNAQEBBQAEggEAEGbE9otKZLmM4dhhqG9tOIxE50ud # hg3HN6IXdtnC7z1e5eMeJxBcyI5BVtJnOGtDSE40oQXuyNYxlRJWicYyZVS6e0mX # /Ga4F4S33wRIbu3vqg8HmmwHKHNYArvmC8ZxNRaS3UPpEOGfmVESBvsCdOg6oBzJ # 8kLmVJM1PHQSM4h43CMNERM1/BiceRnIaOBerf2IxGir2Rpr7bagaNgsmHnIXySm # xWqgfHsVZroZ8KruldWMJQFEIxYtRdFGQLacBC9+kaSOL0i5mpQNnJ8EyBb1NaZF # UoK/L6Zu+sK1+2JKl9k3030XhYUhrvrVIvMNSlM1/gmFnJ8B+yZDQ5TnF6GCEv4w # ghL6BgorBgEEAYI3AwMBMYIS6jCCEuYGCSqGSIb3DQEHAqCCEtcwghLTAgEDMQ8w # DQYJYIZIAWUDBAIBBQAwggFZBgsqhkiG9w0BCRABBKCCAUgEggFEMIIBQAIBAQYK # KwYBBAGEWQoDATAxMA0GCWCGSAFlAwQCAQUABCD8reKtI+HsUz2zwmOP1dryC5RN # oTChNbdn81rlfbaZ6wIGX9zb9bqdGBMyMDIxMDEwNTIyNTAzMC45NjdaMASAAgH0 # oIHYpIHVMIHSMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4G # A1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMS0w # KwYDVQQLEyRNaWNyb3NvZnQgSXJlbGFuZCBPcGVyYXRpb25zIExpbWl0ZWQxJjAk # BgNVBAsTHVRoYWxlcyBUU1MgRVNOOjA4NDItNEJFNi1DMjlBMSUwIwYDVQQDExxN # aWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2aWNloIIOTTCCBPkwggPhoAMCAQICEzMA # AAE5zOjoQ4vwNOEAAAAAATkwDQYJKoZIhvcNAQELBQAwfDELMAkGA1UEBhMCVVMx # EzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoT # FU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUt # U3RhbXAgUENBIDIwMTAwHhcNMjAxMDE1MTcyODIxWhcNMjIwMTEyMTcyODIxWjCB # 0jELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1Jl # ZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEtMCsGA1UECxMk # TWljcm9zb2Z0IElyZWxhbmQgT3BlcmF0aW9ucyBMaW1pdGVkMSYwJAYDVQQLEx1U # aGFsZXMgVFNTIEVTTjowODQyLTRCRTYtQzI5QTElMCMGA1UEAxMcTWljcm9zb2Z0 # IFRpbWUtU3RhbXAgU2VydmljZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC # ggEBANoT+YzjJZMloY6zKgb8onj4Wh+YN09/UMHbuh6gJ1nC5c/f2SqAv0Bc+OYG # 9GndeCL6hWhZtieqPr4YW3+KECThw03I2VwSkE20E/8Oiwlr3Ql5xUJdmvJxHeNh # JhO68UVZirpmx6BClfkMGHRnO9KuTvDNmJKif0rXKxYRbZoRcXL1WfoIOGs87xPO # /rKCM2Fes6jNiljv00svWXqIuV+EKG1egCrsCR9PMkmdpUAV853fK8A88uvgWKiV # 4pvm/lfuxN++NW+nEPX5jjQKRy4ZBqqNO/wdeDZBqqjve/IQI17VaEKSoyrussV8 # 7LKUJ49K5X7Ffx8klv5fos8eyk8CAwEAAaOCARswggEXMB0GA1UdDgQWBBSZnpuG # clTCmdqSirGfRvZQLQYM6TAfBgNVHSMEGDAWgBTVYzpcijGQ80N7fEYbxTNoWoVt # VTBWBgNVHR8ETzBNMEugSaBHhkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtp # L2NybC9wcm9kdWN0cy9NaWNUaW1TdGFQQ0FfMjAxMC0wNy0wMS5jcmwwWgYIKwYB # BQUHAQEETjBMMEoGCCsGAQUFBzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20v # cGtpL2NlcnRzL01pY1RpbVN0YVBDQV8yMDEwLTA3LTAxLmNydDAMBgNVHRMBAf8E # AjAAMBMGA1UdJQQMMAoGCCsGAQUFBwMIMA0GCSqGSIb3DQEBCwUAA4IBAQBYXChq # 8/83GoXNwVrtQ4KIvxANsSb7uvYRiJOhbSDx11jIrlZrUUB3/KckPWrJz9nHH0c/ # 3TKpcpP6vPiDXAixBJaUoJvdcbghWGxYQISybaKM39/Gh+a2NmcVjfW7gxJJyX4h # oiukPvJJAjVpm1o9g8UcBBfCHwcIxexD4WA4HYpSs+POrsIYKLKK9Rrg1opWIxqD # Dat5a8AyJGP/NylKSYaK5aYgXZCDRLFZAn88hC5nvQt7WpmbTEl8a1GcF+lq393U # mBAKst+jyWSa4sE8Ib84yHqg/His1FId7nxNvxIxAG2mwYQqNvrVKMp0+kwCbY3Y # r2uIOz+9/1UK0nDHMIIGcTCCBFmgAwIBAgIKYQmBKgAAAAAAAjANBgkqhkiG9w0B # AQsFADCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNV # BAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEyMDAG # A1UEAxMpTWljcm9zb2Z0IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTAw # HhcNMTAwNzAxMjEzNjU1WhcNMjUwNzAxMjE0NjU1WjB8MQswCQYDVQQGEwJVUzET # MBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMV # TWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1T # dGFtcCBQQ0EgMjAxMDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKkd # Dbx3EYo6IOz8E5f1+n9plGt0VBDVpQoAgoX77XxoSyxfxcPlYcJ2tz5mK1vwFVMn # BDEfQRsalR3OCROOfGEwWbEwRA/xYIiEVEMM1024OAizQt2TrNZzMFcmgqNFDdDq # 9UeBzb8kYDJYYEbyWEeGMoQedGFnkV+BVLHPk0ySwcSmXdFhE24oxhr5hoC732H8 # RsEnHSRnEnIaIYqvS2SJUGKxXf13Hz3wV3WsvYpCTUBR0Q+cBj5nf/VmwAOWRH7v # 0Ev9buWayrGo8noqCjHw2k4GkbaICDXoeByw6ZnNPOcvRLqn9NxkvaQBwSAJk3jN # /LzAyURdXhacAQVPIk0CAwEAAaOCAeYwggHiMBAGCSsGAQQBgjcVAQQDAgEAMB0G # A1UdDgQWBBTVYzpcijGQ80N7fEYbxTNoWoVtVTAZBgkrBgEEAYI3FAIEDB4KAFMA # dQBiAEMAQTALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAW # gBTV9lbLj+iiXGJo0T2UkFvXzpoYxDBWBgNVHR8ETzBNMEugSaBHhkVodHRwOi8v # Y3JsLm1pY3Jvc29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXRf # MjAxMC0wNi0yMy5jcmwwWgYIKwYBBQUHAQEETjBMMEoGCCsGAQUFBzAChj5odHRw # Oi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dF8yMDEw # LTA2LTIzLmNydDCBoAYDVR0gAQH/BIGVMIGSMIGPBgkrBgEEAYI3LgMwgYEwPQYI # KwYBBQUHAgEWMWh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9QS0kvZG9jcy9DUFMv # ZGVmYXVsdC5odG0wQAYIKwYBBQUHAgIwNB4yIB0ATABlAGcAYQBsAF8AUABvAGwA # aQBjAHkAXwBTAHQAYQB0AGUAbQBlAG4AdAAuIB0wDQYJKoZIhvcNAQELBQADggIB # AAfmiFEN4sbgmD+BcQM9naOhIW+z66bM9TG+zwXiqf76V20ZMLPCxWbJat/15/B4 # vceoniXj+bzta1RXCCtRgkQS+7lTjMz0YBKKdsxAQEGb3FwX/1z5Xhc1mCRWS3Tv # QhDIr79/xn/yN31aPxzymXlKkVIArzgPF/UveYFl2am1a+THzvbKegBvSzBEJCI8 # z+0DpZaPWSm8tv0E4XCfMkon/VWvL/625Y4zu2JfmttXQOnxzplmkIz/amJ/3cVK # C5Em4jnsGUpxY517IW3DnKOiPPp/fZZqkHimbdLhnPkd/DjYlPTGpQqWhqS9nhqu # BEKDuLWAmyI4ILUl5WTs9/S/fmNZJQ96LjlXdqJxqgaKD4kWumGnEcua2A5HmoDF # 0M2n0O99g/DhO3EJ3110mCIIYdqwUB5vvfHhAN/nMQekkzr3ZUd46PioSKv33nJ+ # YWtvd6mBy6cJrDm77MbL2IK0cs0d9LiFAR6A+xuJKlQ5slvayA1VmXqHczsI5pgt # 6o3gMy4SKfXAL1QnIffIrE7aKLixqduWsqdCosnPGUFN4Ib5KpqjEWYw07t0Mkvf # Y3v1mYovG8chr1m1rtxEPJdQcdeh0sVV42neV8HR3jDA/czmTfsNv11P6Z0eGTgv # vM9YBS7vDaBQNdrvCScc1bN+NR4Iuto229Nfj950iEkSoYIC1zCCAkACAQEwggEA # oYHYpIHVMIHSMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4G # A1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMS0w # KwYDVQQLEyRNaWNyb3NvZnQgSXJlbGFuZCBPcGVyYXRpb25zIExpbWl0ZWQxJjAk # BgNVBAsTHVRoYWxlcyBUU1MgRVNOOjA4NDItNEJFNi1DMjlBMSUwIwYDVQQDExxN # aWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2aWNloiMKAQEwBwYFKw4DAhoDFQANTZT+ # G96WhI10Px3uSgTGs2WMB6CBgzCBgKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQI # EwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3Nv # ZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBD # QSAyMDEwMA0GCSqGSIb3DQEBBQUAAgUA458VKjAiGA8yMDIxMDEwNjAwNDExNFoY # DzIwMjEwMTA3MDA0MTE0WjB3MD0GCisGAQQBhFkKBAExLzAtMAoCBQDjnxUqAgEA # MAoCAQACAgo7AgH/MAcCAQACAhHCMAoCBQDjoGaqAgEAMDYGCisGAQQBhFkKBAIx # KDAmMAwGCisGAQQBhFkKAwKgCjAIAgEAAgMHoSChCjAIAgEAAgMBhqAwDQYJKoZI # hvcNAQEFBQADgYEAfbYyjFJzQPBiyjUrtN82pjQGw7jJrevWKn1V/R+SCfe2D/2e # LG4Vp6APG+QA0f4+6vOoXBRbyr5Yp4wFGfk4rMEeCIl7GkB+g+WiMTDarNuNPolW # MH0drEys/3TSxlAoyFOLqzBM3je8iduuixXjESmmJhXNZbtRF0VwgIZDzVYxggMN # MIIDCQIBATCBkzB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQ # MA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9u # MSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMAITMwAAATnM # 6OhDi/A04QAAAAABOTANBglghkgBZQMEAgEFAKCCAUowGgYJKoZIhvcNAQkDMQ0G # CyqGSIb3DQEJEAEEMC8GCSqGSIb3DQEJBDEiBCDG0tsYdrPyOQK/zKx91VgYrS3w # NXgx+ymo2Ht4Vgc5rDCB+gYLKoZIhvcNAQkQAi8xgeowgecwgeQwgb0EIDyhjuQ4 # o9ckezFCseKBBa58alUn85p6nyam1KAHD/yfMIGYMIGApH4wfDELMAkGA1UEBhMC # VVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNV # BAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRp # bWUtU3RhbXAgUENBIDIwMTACEzMAAAE5zOjoQ4vwNOEAAAAAATkwIgQgGR80wO/1 # 8XjuFVxaqXRUXzt38WZD6k2FJ36Fb48owR0wDQYJKoZIhvcNAQELBQAEggEASB10 # l4Y4R/xmaF/cJqGPdJ5gpCr4jJP7DSTqRJdBjfiNNKvHWiqEvJhJtFcT8pFUzaye # ybuvT3sTb6cxSCCbtdE9M8/r+MgVSHl83CihdkTcDezxhWudIfRBLMSEvMK5DQfN # 2aNokMkYIX2kk4DAWGFTHYdJNfqidCHyxocAQsBuVLjay5yagJtTEZVDHqQ3vqVI # 16Q4/QuVwB6PMr1Xh/361PamItBSyrBVNois/8/Frp24iKgG0ZnfCp6Nwd3Uh+7Q # +ZDLSf07q0uDnPObMZQER1PGRhyky/WECZRGPNDb8dKgdQK6V+TmDGfzgqqHMzeA # wq8XHYYGUOCQS8QHGA== # SIG # End signature block |