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 (!$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 # MIIdoQYJKoZIhvcNAQcCoIIdkjCCHY4CAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB # gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR # AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUFqrmYoQJGshIovaXWwKEGidk # KU6gghhTMIIEwTCCA6mgAwIBAgITMwAAAMKgCcU3dun2zQAAAAAAwjANBgkqhkiG # 9w0BAQUFADB3MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4G # A1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSEw # HwYDVQQDExhNaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EwHhcNMTYwOTA3MTc1ODUx # WhcNMTgwOTA3MTc1ODUxWjCBsTELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hp # bmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jw # b3JhdGlvbjEMMAoGA1UECxMDQU9DMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjpD # M0IwLTBGNkEtNDExMTElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2Vy # dmljZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJzfPT5gT5YLgF72 # 8Ipv/kMSm0FRtZmMMXMdDBrWM+LOObrNAITBA0w185w4qccTOzXIgsFlOyvvyGfI # jH+4zLekfpL8U7DuccyDVdS3Lg70hYBCEJll0SwAhfpHR1D4NQaeIRnhnlRuSUwy # 7LqOxCE6If90dH0+OaVlxiKHw7R5RgeO50m15BHI+6v9US70IZ8JFqRkfLpk52bh # LNfnossW+CHvAFPVQ0uThMOaoESnJsmban0QaExZvftxreTrz2QQcVw74Y29CYbZ # RUTIy4zIpuM/i5oBLj9mwf9CogC0rQibwWfEvPyiFuOZ/ncDX5I8KVHa4Y1LoFQq # YWk/EEkCAwEAAaOCAQkwggEFMB0GA1UdDgQWBBTjHnnY/MhgLBEZmBJtobBujc6d # rDAfBgNVHSMEGDAWgBQjNPjZUkZwCu1A+3b7syuwwzWzDzBUBgNVHR8ETTBLMEmg # R6BFhkNodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9N # aWNyb3NvZnRUaW1lU3RhbXBQQ0EuY3JsMFgGCCsGAQUFBwEBBEwwSjBIBggrBgEF # BQcwAoY8aHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraS9jZXJ0cy9NaWNyb3Nv # ZnRUaW1lU3RhbXBQQ0EuY3J0MBMGA1UdJQQMMAoGCCsGAQUFBwMIMA0GCSqGSIb3 # DQEBBQUAA4IBAQAoNFRrsA/+bdu8IJvKoxcry0vIPw0qzrUya7ud9MrJ/pp9EO01 # OFrXqbFfuPW0niqZt7hYrs7bzwSlmbBItCkImv0GCLS/3cf0Vl/c0NxUpn8TUjoo # +qwnPF3qRGUzcwrI/3Xl9EfoDlc8jWd2f5FqrjeQdmkdOUmtxSnVt1kbW+Fnjlyl # 1q8aWpkXXgNrBD29iXQV7BklsvtzSVLB32UTZqADm/yzqPC+osWN2eHED2nag1w0 # 51bq++5Pc2mA/UbJeqv+J9VhQwyTGoFdCjE9ygfd7aASPsxiAsRBsNRlylFMjePA # nFZyI0P0rM+CW09Q641SEKIKbT6T1ww+8ByJMIIGATCCA+mgAwIBAgITMwAAAMTp # ifh6gVDp/wAAAAAAxDANBgkqhkiG9w0BAQsFADB+MQswCQYDVQQGEwJVUzETMBEG # A1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWlj # cm9zb2Z0IENvcnBvcmF0aW9uMSgwJgYDVQQDEx9NaWNyb3NvZnQgQ29kZSBTaWdu # aW5nIFBDQSAyMDExMB4XDTE3MDgxMTIwMjAyNFoXDTE4MDgxMTIwMjAyNFowdDEL # MAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1v # bmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEeMBwGA1UEAxMVTWlj # cm9zb2Z0IENvcnBvcmF0aW9uMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC # AQEAiIq4JMMHj5qAeRX8JmD8cogs+vSjl4iWRrejy1+JLzozLh6RePp8qR+CAbV6 # yxq8A8pG68WZ9/sEHfKFCv8ibqHyZz3FJxjlKB/1BJRBY+zjuhWM7ROaNd44cFRv # O+ytRQkwScG+jzCZDMt2yfdzlRZ30Yu7lMcIhSDtHqg18XHC4HQAS4rS3JHr1nj+ # jfqtYIg9vbkfrmKXv8WEsZCu1q8r01T7NdrNcZLmHv/scWvLfwh2dOAQUUjU8QDI # SEyjBzXlWQ39fJzI5lrjhfXWmg8fjqbkhBfB1sqfHQHH/UinE5IzlyFIMvjCJKIA # sr5TyoNuKVuB7zhugPO77BML6wIDAQABo4IBgDCCAXwwHwYDVR0lBBgwFgYKKwYB # BAGCN0wIAQYIKwYBBQUHAwMwHQYDVR0OBBYEFMvWYoTPYDnq/2fCXNLIu6u3wxOY # MFIGA1UdEQRLMEmkRzBFMQ0wCwYDVQQLEwRNT1BSMTQwMgYDVQQFEysyMzAwMTIr # YzgwNGI1ZWEtNDliNC00MjM4LTgzNjItZDg1MWZhMjI1NGZjMB8GA1UdIwQYMBaA # FEhuZOVQBdOCqhc3NyK1bajKdQKVMFQGA1UdHwRNMEswSaBHoEWGQ2h0dHA6Ly93 # d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY0NvZFNpZ1BDQTIwMTFfMjAx # MS0wNy0wOC5jcmwwYQYIKwYBBQUHAQEEVTBTMFEGCCsGAQUFBzAChkVodHRwOi8v # d3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NlcnRzL01pY0NvZFNpZ1BDQTIwMTFf # MjAxMS0wNy0wOC5jcnQwDAYDVR0TAQH/BAIwADANBgkqhkiG9w0BAQsFAAOCAgEA # BhYf21fCUMgjT6JReNft+P3NvdXA8fkbVu1TyGlHBdXEy+zi/JlblV8ROCjABUUT # 4Jp5iLxmq9u76wJVI7c9I3hBba748QBalJmKHMwJldCaHEQwqaUWx7pHW/UrNIuf # j1g3w04cryLKEM3YghCpNfCuIsiPJKaBi98nHORmHYk+Lv9XA03BboOgMuu0sy9Q # Vl0GsRWMyB1jt3MM49Z6Jg8qlkWnMoM+lj5XSXcjif6xEMeK5QgVUcUrWjFbOWqW # qKSIa5Yob/HEruq9RRfMYk6BtVQaR46YpW3AbifG+CcfyO0gqQux8c4LmpTiap1p # g6E2120g/oXV/8O4lzYJ/j0UwZgUqcCGzO+CwatVJEMYtUiFeIbQ+dKdPxnZFInn # jZ9oJIhoO6nHgE4m5wghTGP9nJMVTTO1VmBP10q5OI7/Lt2xX6RDa8l4z7G7a4+D # bIdyquql+5/dGtY5/GTJbT4I5XyDsa28o7p7z5ZWpHpYyxJHYtIh7/w8xDEL9y8+ # ZKU3b2BQP7dEkE+gC4u+flj2x2eHYduemMTIjMtvR+HALpTtsfawMG3sakmo6ZZ2 # yL0IxP479a5zNwayVs8Z1Lv1lMqHHPKAagFPthuBc7PTWyI/OlgY34juZ8RJpy/c # JYs9XtDsNESRHbyRDHaCPu/E2C2hBAKOSPnv3QLPA6IwggYHMIID76ADAgECAgph # Fmg0AAAAAAAcMA0GCSqGSIb3DQEBBQUAMF8xEzARBgoJkiaJk/IsZAEZFgNjb20x # GTAXBgoJkiaJk/IsZAEZFgltaWNyb3NvZnQxLTArBgNVBAMTJE1pY3Jvc29mdCBS # b290IENlcnRpZmljYXRlIEF1dGhvcml0eTAeFw0wNzA0MDMxMjUzMDlaFw0yMTA0 # MDMxMzAzMDlaMHcxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAw # DgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24x # ITAfBgNVBAMTGE1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQTCCASIwDQYJKoZIhvcN # AQEBBQADggEPADCCAQoCggEBAJ+hbLHf20iSKnxrLhnhveLjxZlRI1Ctzt0YTiQP # 7tGn0UytdDAgEesH1VSVFUmUG0KSrphcMCbaAGvoe73siQcP9w4EmPCJzB/LMySH # nfL0Zxws/HvniB3q506jocEjU8qN+kXPCdBer9CwQgSi+aZsk2fXKNxGU7CG0OUo # Ri4nrIZPVVIM5AMs+2qQkDBuh/NZMJ36ftaXs+ghl3740hPzCLdTbVK0RZCfSABK # R2YRJylmqJfk0waBSqL5hKcRRxQJgp+E7VV4/gGaHVAIhQAQMEbtt94jRrvELVSf # rx54QTF3zJvfO4OToWECtR0Nsfz3m7IBziJLVP/5BcPCIAsCAwEAAaOCAaswggGn # MA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFCM0+NlSRnAK7UD7dvuzK7DDNbMP # MAsGA1UdDwQEAwIBhjAQBgkrBgEEAYI3FQEEAwIBADCBmAYDVR0jBIGQMIGNgBQO # rIJgQFYnl+UlE/wq4QpTlVnkpKFjpGEwXzETMBEGCgmSJomT8ixkARkWA2NvbTEZ # MBcGCgmSJomT8ixkARkWCW1pY3Jvc29mdDEtMCsGA1UEAxMkTWljcm9zb2Z0IFJv # b3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5ghB5rRahSqClrUxzWPQHEy5lMFAGA1Ud # HwRJMEcwRaBDoEGGP2h0dHA6Ly9jcmwubWljcm9zb2Z0LmNvbS9wa2kvY3JsL3By # b2R1Y3RzL21pY3Jvc29mdHJvb3RjZXJ0LmNybDBUBggrBgEFBQcBAQRIMEYwRAYI # KwYBBQUHMAKGOGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2kvY2VydHMvTWlj # cm9zb2Z0Um9vdENlcnQuY3J0MBMGA1UdJQQMMAoGCCsGAQUFBwMIMA0GCSqGSIb3 # DQEBBQUAA4ICAQAQl4rDXANENt3ptK132855UU0BsS50cVttDBOrzr57j7gu1BKi # jG1iuFcCy04gE1CZ3XpA4le7r1iaHOEdAYasu3jyi9DsOwHu4r6PCgXIjUji8FMV # 3U+rkuTnjWrVgMHmlPIGL4UD6ZEqJCJw+/b85HiZLg33B+JwvBhOnY5rCnKVuKE5 # nGctxVEO6mJcPxaYiyA/4gcaMvnMMUp2MT0rcgvI6nA9/4UKE9/CCmGO8Ne4F+tO # i3/FNSteo7/rvH0LQnvUU3Ih7jDKu3hlXFsBFwoUDtLaFJj1PLlmWLMtL+f5hYbM # UVbonXCUbKw5TNT2eb+qGHpiKe+imyk0BncaYsk9Hm0fgvALxyy7z0Oz5fnsfbXj # pKh0NbhOxXEjEiZ2CzxSjHFaRkMUvLOzsE1nyJ9C/4B5IYCeFTBm6EISXhrIniIh # 0EPpK+m79EjMLNTYMoBMJipIJF9a6lbvpt6Znco6b72BJ3QGEe52Ib+bgsEnVLax # aj2JoXZhtG6hE6a/qkfwEm/9ijJssv7fUciMI8lmvZ0dhxJkAj0tr1mPuOQh5bWw # ymO0eFQF1EEuUKyUsKV4q7OglnUa2ZKHE3UiLzKoCG6gW4wlv6DvhMoh1useT8ma # 7kng9wFlb4kLfchpyOZu6qeXzjEp/w7FW1zYTRuh2Povnj8uVRZryROj/TCCB3ow # ggVioAMCAQICCmEOkNIAAAAAAAMwDQYJKoZIhvcNAQELBQAwgYgxCzAJBgNVBAYT # AlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYD # VQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xMjAwBgNVBAMTKU1pY3Jvc29mdCBS # b290IENlcnRpZmljYXRlIEF1dGhvcml0eSAyMDExMB4XDTExMDcwODIwNTkwOVoX # DTI2MDcwODIxMDkwOVowfjELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0 # b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3Jh # dGlvbjEoMCYGA1UEAxMfTWljcm9zb2Z0IENvZGUgU2lnbmluZyBQQ0EgMjAxMTCC # AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAKvw+nIQHC6t2G6qghBNNLry # tlghn0IbKmvpWlCquAY4GgRJun/DDB7dN2vGEtgL8DjCmQawyDnVARQxQtOJDXlk # h36UYCRsr55JnOloXtLfm1OyCizDr9mpK656Ca/XllnKYBoF6WZ26DJSJhIv56sI # UM+zRLdd2MQuA3WraPPLbfM6XKEW9Ea64DhkrG5kNXimoGMPLdNAk/jj3gcN1Vx5 # pUkp5w2+oBN3vpQ97/vjK1oQH01WKKJ6cuASOrdJXtjt7UORg9l7snuGG9k+sYxd # 6IlPhBryoS9Z5JA7La4zWMW3Pv4y07MDPbGyr5I4ftKdgCz1TlaRITUlwzluZH9T # upwPrRkjhMv0ugOGjfdf8NBSv4yUh7zAIXQlXxgotswnKDglmDlKNs98sZKuHCOn # qWbsYR9q4ShJnV+I4iVd0yFLPlLEtVc/JAPw0XpbL9Uj43BdD1FGd7P4AOG8rAKC # X9vAFbO9G9RVS+c5oQ/pI0m8GLhEfEXkwcNyeuBy5yTfv0aZxe/CHFfbg43sTUkw # p6uO3+xbn6/83bBm4sGXgXvt1u1L50kppxMopqd9Z4DmimJ4X7IvhNdXnFy/dygo # 8e1twyiPLI9AN0/B4YVEicQJTMXUpUMvdJX3bvh4IFgsE11glZo+TzOE2rCIF96e # TvSWsLxGoGyY0uDWiIwLAgMBAAGjggHtMIIB6TAQBgkrBgEEAYI3FQEEAwIBADAd # BgNVHQ4EFgQUSG5k5VAF04KqFzc3IrVtqMp1ApUwGQYJKwYBBAGCNxQCBAweCgBT # AHUAYgBDAEEwCwYDVR0PBAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgw # FoAUci06AjGQQ7kUBU7h6qfHMdEjiTQwWgYDVR0fBFMwUTBPoE2gS4ZJaHR0cDov # L2NybC5taWNyb3NvZnQuY29tL3BraS9jcmwvcHJvZHVjdHMvTWljUm9vQ2VyQXV0 # MjAxMV8yMDExXzAzXzIyLmNybDBeBggrBgEFBQcBAQRSMFAwTgYIKwYBBQUHMAKG # Qmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2kvY2VydHMvTWljUm9vQ2VyQXV0 # MjAxMV8yMDExXzAzXzIyLmNydDCBnwYDVR0gBIGXMIGUMIGRBgkrBgEEAYI3LgMw # gYMwPwYIKwYBBQUHAgEWM2h0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMv # ZG9jcy9wcmltYXJ5Y3BzLmh0bTBABggrBgEFBQcCAjA0HjIgHQBMAGUAZwBhAGwA # XwBwAG8AbABpAGMAeQBfAHMAdABhAHQAZQBtAGUAbgB0AC4gHTANBgkqhkiG9w0B # AQsFAAOCAgEAZ/KGpZjgVHkaLtPYdGcimwuWEeFjkplCln3SeQyQwWVfLiw++MNy # 0W2D/r4/6ArKO79HqaPzadtjvyI1pZddZYSQfYtGUFXYDJJ80hpLHPM8QotS0LD9 # a+M+By4pm+Y9G6XUtR13lDni6WTJRD14eiPzE32mkHSDjfTLJgJGKsKKELukqQUM # m+1o+mgulaAqPyprWEljHwlpblqYluSD9MCP80Yr3vw70L01724lruWvJ+3Q3fMO # r5kol5hNDj0L8giJ1h/DMhji8MUtzluetEk5CsYKwsatruWy2dsViFFFWDgycSca # f7H0J/jeLDogaZiyWYlobm+nt3TDQAUGpgEqKD6CPxNNZgvAs0314Y9/HG8VfUWn # duVAKmWjw11SYobDHWM2l4bf2vP48hahmifhzaWX0O5dY0HjWwechz4GdwbRBrF1 # HxS+YWG18NzGGwS+30HHDiju3mUv7Jf2oVyW2ADWoUa9WfOXpQlLSBCZgB/QACnF # sZulP0V3HjXG0qKin3p6IvpIlR+r+0cjgPWe+L9rt0uX4ut1eBrs6jeZeRhL/9az # I2h15q/6/IvrC4DqaTuv/DDtBEyO3991bWORPdGdVk5Pv4BXIqF4ETIheu9BCrE/ # +6jMpF3BoYibV3FWTkhFwELJm3ZbCoBIa/15n8G9bW1qyVJzEw16UM0xggS4MIIE # tAIBATCBlTB+MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4G # A1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSgw # JgYDVQQDEx9NaWNyb3NvZnQgQ29kZSBTaWduaW5nIFBDQSAyMDExAhMzAAAAxOmJ # +HqBUOn/AAAAAADEMAkGBSsOAwIaBQCggcwwGQYJKoZIhvcNAQkDMQwGCisGAQQB # gjcCAQQwHAYKKwYBBAGCNwIBCzEOMAwGCisGAQQBgjcCARUwIwYJKoZIhvcNAQkE # MRYEFD6A2e2JJLi8ON12ps9Nn52hE6n0MGwGCisGAQQBgjcCAQwxXjBcoByAGgBD # AFEARABQAG8AdwBlAHIAUwBoAGUAbABsoTyAOmh0dHBzOi8vd3d3LnBvd2Vyc2hl # bGxnYWxsZXJ5LmNvbS9wYWNrYWdlcy9DUURQb3dlclNoZWxsLyAwDQYJKoZIhvcN # AQEBBQAEggEAJFyt6b+AykBGeqjj8mvMNbBr9UHXHNoKUmOXjdXq3Xq7gqPZhrqU # TVnpQF+HlmL43uzIjf75wraaio5sGtOgsU4GoZyjoyNoZcVxcXGeRLTMXfwdTLOw # e5CylO8Mh9SaSphJMTNlQB+29Toclja+nDGrf3NZWPQO537v9xme2DiCCaGxEcgG # t/9e5xDrwDZ/rQc5ap3uLuGuBRMaaoOFhwqlj1evisnTLjYJbhEDthnofz7omZER # RHQeB8q6bxk7TjhSioLAncg2jpc3pD29YCkZ18SC/0f+KCLcp0czH0vNpw4ey2Zr # tuFJc/Vvg6yXNv+4APGUZHUICb+VWwdwnqGCAigwggIkBgkqhkiG9w0BCQYxggIV # MIICEQIBATCBjjB3MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQ # MA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9u # MSEwHwYDVQQDExhNaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0ECEzMAAADCoAnFN3bp # 9s0AAAAAAMIwCQYFKw4DAhoFAKBdMBgGCSqGSIb3DQEJAzELBgkqhkiG9w0BBwEw # HAYJKoZIhvcNAQkFMQ8XDTE4MDQyNzAxMDEyOFowIwYJKoZIhvcNAQkEMRYEFHV5 # oA2mErr//Asjj25PO0Stu6SqMA0GCSqGSIb3DQEBBQUABIIBAFt6UaWN9S4e4z/B # fe7B6ZehWwHcX2HLaSbNvX9+b6mMJVZ9JPHYgivuquE5Fm/ImvG9iTVGIMspOKfv # n/yFxOUI6OYP/0KU+4XnEhXCIkBWEuyH1txjh1J+yA7iDI2KoaJ9wX0Y37jPwR66 # SCwaC92izA3StwHLOhz1+CFZzThsqOsBd/m4THbuvzABusN2xZWgfQOF+8OzmkaI # HT3cDNqq4HBmfxpR+GrDyF64ZM3MVsA2Hm6yJCB7fmKNxIfRZTydIWJrpSw/CW0k # gqxRqDawzIO3iO5BV8D/F/GW1YlDVCAlegsoV87mgfn94V0ZeiTqWuiHP098+1LL # Gw8ox2c= # SIG # End signature block |