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) { Add-Type -AssemblyName System.Web $resourceUrl = $WebResource $redirectUrl = "https://cqd.lync.com/spd/" $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 { $Script:Configuration = Invoke-RestMethod -Uri 'https://repository.cqd.lync.com/clientconfiguration' -Method Get -SessionVariable WebSession -UserAgent "CQDPowerShell V1.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 V1.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 V1.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 ($NowTimeUTC.ToUniversalTime() -gt $JWTExpireTime) { Write-Host "Token Expired" 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 V1.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.DayOfYear + "]," } 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 = “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 = “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 = “Day Of Year” $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 = "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 = "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 = "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 = "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 = "Transport" $row.FilterValue = $TransportV.Value $row.FilterCaption = "NA" $row.FilterOperand = $TransportV.Operand $row.FilterUnionGroup = $TransportV.UnionGroup $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 ) 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":[ '@ #FilterRow $FilterRows = @" {"DataModelName":"[AllStreams].[$FilterName]","Caption":"$CaptionText","Value":"[$Filter.Value]","Operand":$Filter.Operand,"UnionGroup":"$Filter.UnionGroup"} "@ #DimensionRow $DimensionRow = @" {"DataModelName":"$Dimension"} "@ #MeasureRow $MeasureRow = @" {"DataModelName":"$Measure"} "@ #Dimension Sections $StartDimensions = @' "Dimensions":[ '@ #Measure Sections $StartMeasures = @' "Measurements":[ '@ ###############Loop Arrays of Each Type $FilterCount = $Filters.Count - 1 $FF = 0 foreach ($Filter in $Filters) { $FLN = $Filter.FilterName $FCN = $Filter.FilterCaption $FVE = $Filter.FilterValue $FOP = $Filter.FilterOperand $UNGP = $Filter.FilterUnionGroup if ($FF -eq $FilterCount) { $FilterRowArray += @" {"DataModelName":"[AllStreams].[$FLN]","Caption":"$FCN","Value":"$FVE","Operand":$FOP,"UnionGroup":"$UNGP"} "@ } else { $FilterRowArray += @" {"DataModelName":"[AllStreams].[$FLN]","Caption":"$FCN","Value":"$FVE","Operand":$FOP,"UnionGroup":"$UNGP"}, "@ } $FF += 1 } #Dimensions $DimCount = $DimensionsArray.Count - 1 $DD = 0 foreach ($DimensionLoop in $DimensionsArray) { if ($DD -eq $DimCount) { $DimensionRowArray += @" {"DataModelName":"$DimensionLoop"} "@ } else { $DimensionRowArray += @" {"DataModelName":"$DimensionLoop"}, "@ } $DD += 1 } $MeaCount = $MeasurementsArray.Count - 1 $MM = 0 foreach ($MeasureLoop in $MeasurementsArray) { if ($MM -eq $MeaCount) { $MeasureRowArray += @" {"DataModelName":"$MeasureLoop"} "@ } else { $MeasureRowArray += @" {"DataModelName":"$MeasureLoop"}, "@ } $MM += 1 } $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 $IRM = Invoke-RestMethod -Uri ('{0}RunQuery' -f $DataServiceBaseUrl) -WebSession $WebSession -Method Post -Body $Query -ContentType 'application/json' -UserAgent "CQDPowerShell V1.0" $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 ($null -eq $ResultItem) #if(1 -eq 2) { Write-Host "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 # MIIdgAYJKoZIhvcNAQcCoIIdcTCCHW0CAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB # gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR # AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUEVzv47lMYbjBdBl5Fk6Jj5jN # og+gghhUMIIEwjCCA6qgAwIBAgITMwAAAMRudtBNPf6pZQAAAAAAxDANBgkqhkiG # 9w0BAQUFADB3MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4G # A1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSEw # HwYDVQQDExhNaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EwHhcNMTYwOTA3MTc1ODUy # WhcNMTgwOTA3MTc1ODUyWjCBsjELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hp # bmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jw # b3JhdGlvbjEMMAoGA1UECxMDQU9DMScwJQYDVQQLEx5uQ2lwaGVyIERTRSBFU046 # MjEzNy0zN0EwLTRBQUExJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNl # cnZpY2UwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCoA5rFUpl2jKM9 # /L26GuVj6Beo87YdPTwuOL0C+QObtrYvih7LDNDAeWLw+wYlSkAmfmaSXFpiRHM1 # dBzq+VcuF8YGmZm/LKWIAB3VTj6df05JH8kgtp4gN2myPTR+rkwoMoQ3muR7zb1n # vNiLsEpgJ2EuwX5M/71uYrK6DHAPbbD3ryFizZAfqYcGUWuDhEE6ZV+onexUulZ6 # DK6IoLjtQvUbH1ZMEWvNVTliPYOgNYLTIcJ5mYphnUMABoKdvGDcVpSmGn6sLKGg # iFC82nun9h7koj7+ZpSHElsLwhWQiGVWCRVk8ZMbec+qhu+/9HwzdVJYb4HObmwN # Daqpqe17AgMBAAGjggEJMIIBBTAdBgNVHQ4EFgQUiAUj6xG9EI77i5amFSZrXv1V # 3lAwHwYDVR0jBBgwFoAUIzT42VJGcArtQPt2+7MrsMM1sw8wVAYDVR0fBE0wSzBJ # oEegRYZDaHR0cDovL2NybC5taWNyb3NvZnQuY29tL3BraS9jcmwvcHJvZHVjdHMv # TWljcm9zb2Z0VGltZVN0YW1wUENBLmNybDBYBggrBgEFBQcBAQRMMEowSAYIKwYB # BQUHMAKGPGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2kvY2VydHMvTWljcm9z # b2Z0VGltZVN0YW1wUENBLmNydDATBgNVHSUEDDAKBggrBgEFBQcDCDANBgkqhkiG # 9w0BAQUFAAOCAQEAcDh+kjmXvCnoEO5AcUWfp4/4fWCqiBQL8uUFq6cuBuYp8ML4 # UyHSLKNPOoJmzzy1OT3GFGYrmprgO6c2d1tzuSaN3HeFGENXDbn7N2RBvJtSl0Uk # ahSyak4TsRUPk/WwMQ0GOGNbxjolrOR41LVsSmHVnn8IWDOCWBj1c+1jkPkzG51j # CiAnWzHU1Q25A/0txrhLYjNtI4P3f0T0vv65X7rZAIz3ecQS/EglmADfQk/zrLgK # qJdxZKy3tXS7+35zIrDegdAH2G7d3jvCNTjatrV7cxKH+ZX9oEsFl10uh/U83KA2 # QiQJQMtbjGSzQV2xRpcNf2GpHBRPW0sK4yL3wzCCBgEwggPpoAMCAQICEzMAAADE # 6Yn4eoFQ6f8AAAAAAMQwDQYJKoZIhvcNAQELBQAwfjELMAkGA1UEBhMCVVMxEzAR # BgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1p # Y3Jvc29mdCBDb3Jwb3JhdGlvbjEoMCYGA1UEAxMfTWljcm9zb2Z0IENvZGUgU2ln # bmluZyBQQ0EgMjAxMTAeFw0xNzA4MTEyMDIwMjRaFw0xODA4MTEyMDIwMjRaMHQx # CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRt # b25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xHjAcBgNVBAMTFU1p # Y3Jvc29mdCBDb3Jwb3JhdGlvbjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC # ggEBAIiKuCTDB4+agHkV/CZg/HKILPr0o5eIlka3o8tfiS86My4ekXj6fKkfggG1 # essavAPKRuvFmff7BB3yhQr/Im6h8mc9xScY5Sgf9QSUQWPs47oVjO0TmjXeOHBU # bzvsrUUJMEnBvo8wmQzLdsn3c5UWd9GLu5THCIUg7R6oNfFxwuB0AEuK0tyR69Z4 # /o36rWCIPb25H65il7/FhLGQrtavK9NU+zXazXGS5h7/7HFry38IdnTgEFFI1PEA # yEhMowc15VkN/XycyOZa44X11poPH46m5IQXwdbKnx0Bx/1IpxOSM5chSDL4wiSi # ALK+U8qDbilbge84boDzu+wTC+sCAwEAAaOCAYAwggF8MB8GA1UdJQQYMBYGCisG # AQQBgjdMCAEGCCsGAQUFBwMDMB0GA1UdDgQWBBTL1mKEz2A56v9nwlzSyLurt8MT # mDBSBgNVHREESzBJpEcwRTENMAsGA1UECxMETU9QUjE0MDIGA1UEBRMrMjMwMDEy # K2M4MDRiNWVhLTQ5YjQtNDIzOC04MzYyLWQ4NTFmYTIyNTRmYzAfBgNVHSMEGDAW # gBRIbmTlUAXTgqoXNzcitW2oynUClTBUBgNVHR8ETTBLMEmgR6BFhkNodHRwOi8v # d3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NybC9NaWNDb2RTaWdQQ0EyMDExXzIw # MTEtMDctMDguY3JsMGEGCCsGAQUFBwEBBFUwUzBRBggrBgEFBQcwAoZFaHR0cDov # L3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9jZXJ0cy9NaWNDb2RTaWdQQ0EyMDEx # XzIwMTEtMDctMDguY3J0MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggIB # AAYWH9tXwlDII0+iUXjX7fj9zb3VwPH5G1btU8hpRwXVxMvs4vyZW5VfETgowAVF # E+CaeYi8Zqvbu+sCVSO3PSN4QW2u+PEAWpSZihzMCZXQmhxEMKmlFse6R1v1KzSL # n49YN8NOHK8iyhDN2IIQqTXwriLIjySmgYvfJxzkZh2JPi7/VwNNwW6DoDLrtLMv # UFZdBrEVjMgdY7dzDOPWeiYPKpZFpzKDPpY+V0l3I4n+sRDHiuUIFVHFK1oxWzlq # lqikiGuWKG/xxK7qvUUXzGJOgbVUGkeOmKVtwG4nxvgnH8jtIKkLsfHOC5qU4mqd # aYOhNtdtIP6F1f/DuJc2Cf49FMGYFKnAhszvgsGrVSRDGLVIhXiG0PnSnT8Z2RSJ # 542faCSIaDupx4BOJucIIUxj/ZyTFU0ztVZgT9dKuTiO/y7dsV+kQ2vJeM+xu2uP # g2yHcqrqpfuf3RrWOfxkyW0+COV8g7GtvKO6e8+WVqR6WMsSR2LSIe/8PMQxC/cv # PmSlN29gUD+3RJBPoAuLvn5Y9sdnh2HbnpjEyIzLb0fhwC6U7bH2sDBt7GpJqOmW # dsi9CMT+O/WuczcGslbPGdS79ZTKhxzygGoBT7YbgXOz01siPzpYGN+I7mfESacv # 3CWLPV7Q7DREkR28kQx2gj7vxNgtoQQCjkj5790CzwOiMIIGBzCCA++gAwIBAgIK # YRZoNAAAAAAAHDANBgkqhkiG9w0BAQUFADBfMRMwEQYKCZImiZPyLGQBGRYDY29t # MRkwFwYKCZImiZPyLGQBGRYJbWljcm9zb2Z0MS0wKwYDVQQDEyRNaWNyb3NvZnQg # Um9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcNMDcwNDAzMTI1MzA5WhcNMjEw # NDAzMTMwMzA5WjB3MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQ # MA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9u # MSEwHwYDVQQDExhNaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EwggEiMA0GCSqGSIb3 # DQEBAQUAA4IBDwAwggEKAoIBAQCfoWyx39tIkip8ay4Z4b3i48WZUSNQrc7dGE4k # D+7Rp9FMrXQwIBHrB9VUlRVJlBtCkq6YXDAm2gBr6Hu97IkHD/cOBJjwicwfyzMk # h53y9GccLPx754gd6udOo6HBI1PKjfpFzwnQXq/QsEIEovmmbJNn1yjcRlOwhtDl # KEYuJ6yGT1VSDOQDLPtqkJAwbofzWTCd+n7Wl7PoIZd++NIT8wi3U21StEWQn0gA # SkdmEScpZqiX5NMGgUqi+YSnEUcUCYKfhO1VeP4Bmh1QCIUAEDBG7bfeI0a7xC1U # n68eeEExd8yb3zuDk6FhArUdDbH895uyAc4iS1T/+QXDwiALAgMBAAGjggGrMIIB # pzAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBQjNPjZUkZwCu1A+3b7syuwwzWz # DzALBgNVHQ8EBAMCAYYwEAYJKwYBBAGCNxUBBAMCAQAwgZgGA1UdIwSBkDCBjYAU # DqyCYEBWJ5flJRP8KuEKU5VZ5KShY6RhMF8xEzARBgoJkiaJk/IsZAEZFgNjb20x # GTAXBgoJkiaJk/IsZAEZFgltaWNyb3NvZnQxLTArBgNVBAMTJE1pY3Jvc29mdCBS # b290IENlcnRpZmljYXRlIEF1dGhvcml0eYIQea0WoUqgpa1Mc1j0BxMuZTBQBgNV # HR8ESTBHMEWgQ6BBhj9odHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2NybC9w # cm9kdWN0cy9taWNyb3NvZnRyb290Y2VydC5jcmwwVAYIKwYBBQUHAQEESDBGMEQG # CCsGAQUFBzAChjhodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01p # Y3Jvc29mdFJvb3RDZXJ0LmNydDATBgNVHSUEDDAKBggrBgEFBQcDCDANBgkqhkiG # 9w0BAQUFAAOCAgEAEJeKw1wDRDbd6bStd9vOeVFNAbEudHFbbQwTq86+e4+4LtQS # ooxtYrhXAstOIBNQmd16QOJXu69YmhzhHQGGrLt48ovQ7DsB7uK+jwoFyI1I4vBT # Fd1Pq5Lk541q1YDB5pTyBi+FA+mRKiQicPv2/OR4mS4N9wficLwYTp2Oawpylbih # OZxnLcVRDupiXD8WmIsgP+IHGjL5zDFKdjE9K3ILyOpwPf+FChPfwgphjvDXuBfr # Tot/xTUrXqO/67x9C0J71FNyIe4wyrt4ZVxbARcKFA7S2hSY9Ty5ZlizLS/n+YWG # zFFW6J1wlGysOUzU9nm/qhh6YinvopspNAZ3GmLJPR5tH4LwC8csu89Ds+X57H21 # 46SodDW4TsVxIxImdgs8UoxxWkZDFLyzs7BNZ8ifQv+AeSGAnhUwZuhCEl4ayJ4i # IdBD6Svpu/RIzCzU2DKATCYqSCRfWupW76bemZ3KOm+9gSd0BhHudiG/m4LBJ1S2 # sWo9iaF2YbRuoROmv6pH8BJv/YoybLL+31HIjCPJZr2dHYcSZAI9La9Zj7jkIeW1 # sMpjtHhUBdRBLlCslLCleKuzoJZ1GtmShxN1Ii8yqAhuoFuMJb+g74TKIdbrHk/J # mu5J4PcBZW+JC33Iacjmbuqnl84xKf8OxVtc2E0bodj6L54/LlUWa8kTo/0wggd6 # MIIFYqADAgECAgphDpDSAAAAAAADMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYDVQQG # EwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwG # A1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQg # Um9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgMjAxMTAeFw0xMTA3MDgyMDU5MDla # Fw0yNjA3MDgyMTA5MDlaMH4xCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5n # dG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9y # YXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMTEw # ggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCr8PpyEBwurdhuqoIQTTS6 # 8rZYIZ9CGypr6VpQqrgGOBoESbp/wwwe3TdrxhLYC/A4wpkGsMg51QEUMULTiQ15 # ZId+lGAkbK+eSZzpaF7S35tTsgosw6/ZqSuuegmv15ZZymAaBelmdugyUiYSL+er # CFDPs0S3XdjELgN1q2jzy23zOlyhFvRGuuA4ZKxuZDV4pqBjDy3TQJP4494HDdVc # eaVJKecNvqATd76UPe/74ytaEB9NViiienLgEjq3SV7Y7e1DkYPZe7J7hhvZPrGM # XeiJT4Qa8qEvWeSQOy2uM1jFtz7+MtOzAz2xsq+SOH7SnYAs9U5WkSE1JcM5bmR/ # U7qcD60ZI4TL9LoDho33X/DQUr+MlIe8wCF0JV8YKLbMJyg4JZg5SjbPfLGSrhwj # p6lm7GEfauEoSZ1fiOIlXdMhSz5SxLVXPyQD8NF6Wy/VI+NwXQ9RRnez+ADhvKwC # gl/bwBWzvRvUVUvnOaEP6SNJvBi4RHxF5MHDcnrgcuck379GmcXvwhxX24ON7E1J # MKerjt/sW5+v/N2wZuLBl4F77dbtS+dJKacTKKanfWeA5opieF+yL4TXV5xcv3co # KPHtbcMojyyPQDdPweGFRInECUzF1KVDL3SV9274eCBYLBNdYJWaPk8zhNqwiBfe # nk70lrC8RqBsmNLg1oiMCwIDAQABo4IB7TCCAekwEAYJKwYBBAGCNxUBBAMCAQAw # HQYDVR0OBBYEFEhuZOVQBdOCqhc3NyK1bajKdQKVMBkGCSsGAQQBgjcUAgQMHgoA # UwB1AGIAQwBBMAsGA1UdDwQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQY # MBaAFHItOgIxkEO5FAVO4eqnxzHRI4k0MFoGA1UdHwRTMFEwT6BNoEuGSWh0dHA6 # Ly9jcmwubWljcm9zb2Z0LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY1Jvb0NlckF1 # dDIwMTFfMjAxMV8wM18yMi5jcmwwXgYIKwYBBQUHAQEEUjBQME4GCCsGAQUFBzAC # hkJodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1Jvb0NlckF1 # dDIwMTFfMjAxMV8wM18yMi5jcnQwgZ8GA1UdIASBlzCBlDCBkQYJKwYBBAGCNy4D # MIGDMD8GCCsGAQUFBwIBFjNodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3Bz # L2RvY3MvcHJpbWFyeWNwcy5odG0wQAYIKwYBBQUHAgIwNB4yIB0ATABlAGcAYQBs # AF8AcABvAGwAaQBjAHkAXwBzAHQAYQB0AGUAbQBlAG4AdAAuIB0wDQYJKoZIhvcN # AQELBQADggIBAGfyhqWY4FR5Gi7T2HRnIpsLlhHhY5KZQpZ90nkMkMFlXy4sPvjD # ctFtg/6+P+gKyju/R6mj82nbY78iNaWXXWWEkH2LRlBV2AySfNIaSxzzPEKLUtCw # /WvjPgcuKZvmPRul1LUdd5Q54ulkyUQ9eHoj8xN9ppB0g430yyYCRirCihC7pKkF # DJvtaPpoLpWgKj8qa1hJYx8JaW5amJbkg/TAj/NGK978O9C9Ne9uJa7lryft0N3z # Dq+ZKJeYTQ49C/IIidYfwzIY4vDFLc5bnrRJOQrGCsLGra7lstnbFYhRRVg4MnEn # Gn+x9Cf43iw6IGmYslmJaG5vp7d0w0AFBqYBKig+gj8TTWYLwLNN9eGPfxxvFX1F # p3blQCplo8NdUmKGwx1jNpeG39rz+PIWoZon4c2ll9DuXWNB41sHnIc+BncG0Qax # dR8UvmFhtfDcxhsEvt9Bxw4o7t5lL+yX9qFcltgA1qFGvVnzl6UJS0gQmYAf0AAp # xbGbpT9Fdx41xtKiop96eiL6SJUfq/tHI4D1nvi/a7dLl+LrdXga7Oo3mXkYS//W # syNodeav+vyL6wuA6mk7r/ww7QRMjt/fdW1jkT3RnVZOT7+AVyKheBEyIXrvQQqx # P/uozKRdwaGIm1dxVk5IRcBCyZt2WwqASGv9eZ/BvW1taslScxMNelDNMYIEljCC # BJICAQEwgZUwfjELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAO # BgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEo # MCYGA1UEAxMfTWljcm9zb2Z0IENvZGUgU2lnbmluZyBQQ0EgMjAxMQITMwAAAMTp # ifh6gVDp/wAAAAAAxDAJBgUrDgMCGgUAoIGqMBkGCSqGSIb3DQEJAzEMBgorBgEE # AYI3AgEEMBwGCisGAQQBgjcCAQsxDjAMBgorBgEEAYI3AgEVMCMGCSqGSIb3DQEJ # BDEWBBQIPtURyAzELaqB2R/JUF1UV04RMTBKBgorBgEEAYI3AgEMMTwwOqAcgBoA # QwBRAEQAUABPAFcARQBSAFMASABFAEwATKEagBhodHRwOi8vd3d3Lm1pY3Jvc29m # dC5jb20wDQYJKoZIhvcNAQEBBQAEggEAZPPJPllIaAFZ8UIGKytahJ6OyZsIwKs5 # VQIh2un0Xt+NTBJNY99WZOf+YvMDKzPJ2I9XHymmttni49gUixM50j8IxNna1c5I # +UYWIGXggDy11lbCTK737ou00GwdPKke0S6m8fAZrPJbZpqdim5AF4fat6Z/99Qg # K0iUeZ/Filq0y6x/vyOtXci8vT55gCTHp761RK62saNG1UUZ4P+PuV0yYhC79kK+ # 4K/ANl/+wWw+VfHn5cLnxR089kthFvfRcir5fMy4uudtuYjzqN7dJoaOlLQXPn2K # 4hm+x/d6D6Ccu3Cawo9kN9d90pKh0Z58buF5zg5sA9v2upUJrjEZp6GCAigwggIk # BgkqhkiG9w0BCQYxggIVMIICEQIBATCBjjB3MQswCQYDVQQGEwJVUzETMBEGA1UE # CBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9z # b2Z0IENvcnBvcmF0aW9uMSEwHwYDVQQDExhNaWNyb3NvZnQgVGltZS1TdGFtcCBQ # Q0ECEzMAAADEbnbQTT3+qWUAAAAAAMQwCQYFKw4DAhoFAKBdMBgGCSqGSIb3DQEJ # AzELBgkqhkiG9w0BBwEwHAYJKoZIhvcNAQkFMQ8XDTE4MDMyOTIwNDUzM1owIwYJ # KoZIhvcNAQkEMRYEFDP6O8mm2y0hemRNvER5eG1RFWs+MA0GCSqGSIb3DQEBBQUA # BIIBAKBcWo8Do1ayDQjkCzgThCyqZKlVwf0Dc6d+MN9qHFTiEvUw2rTw3EEER8Lk # y+RYzchCDdQBYhF1o7f8XDu3stotf2qrGKTOtm4okV5w4P6n7zs/T8DTmy90htk9 # KmoN62LP7+q6H/KbOMMv/pxEaWVm51n2+MdJsYeOsGXsKMvpECxSHFdr1s6iRSPY # yBa624ZAOzfEwXIqlwOj6ZBAIdsobkm6o/uvRWnLt47dots8hJj+na/jB2jNy6VD # YkiW2oN3U+qgelblPaq44TdalVO1ZcMFtpPj4UPy9S+IUIz+EXbQHoWVVgf01gP9 # CXrckqGl97l6F+tUIjWzBW2AGIY= # SIG # End signature block |