Obs/bin/SBRPClient/content/Microsoft.AzureStack.Diagnostics.SupportService.psm1
<###################################################
# # # Copyright (c) Microsoft. All rights reserved. # # # ##################################################> if (Test-Path "$PSScriptRoot\bin") { # Loaded from WindowsPowerShell\Modules $clientBinaryDirectory = "$PSScriptRoot\bin" } elseif (Test-Path "$PSScriptRoot\..\lib\net46") { # Loaded from NugetStore $clientBinaryDirectory = "$PSScriptRoot\..\lib\net46" } elseif (Test-Path "$PSScriptRoot\..\..\lib") { # Loaded from NugetStore : used in Remote Support $clientBinaryDirectory = "$PSScriptRoot\..\..\lib" } elseif (Test-Path "$PSScriptRoot\..\lib") { # Loaded from NugetStore : used in Log Collection $clientBinaryDirectory = "$PSScriptRoot\..\lib" } else { throw "Unknown $PSScriptRoot" } Write-Verbose "Client Binary resolved to - [$clientBinaryDirectory], All the binaries will be loaded from here" -Verbose Add-Type -Path "$clientBinaryDirectory\Newtonsoft.Json.dll" -ErrorAction Stop -Verbose:$false | Out-Null Add-Type -Path "$clientBinaryDirectory\Microsoft.AzureStack.SupportBridge.LogCollector.Client.Contract.dll" -ErrorAction Stop -Verbose:$false | Out-Null Add-Type -Path "$clientBinaryDirectory\Microsoft.AzureStack.SupportBridge.LogCollector.Client.dll" -ErrorAction Stop -Verbose:$false | Out-Null Add-Type -Path "$clientBinaryDirectory\Microsoft.AzureStack.SupportBridge.LogCollector.Client.Models.dll" -ErrorAction Stop -Verbose:$false | Out-Null Add-Type -Path "$clientBinaryDirectory\Microsoft.AzureStack.Common.Operations.Contract.dll" -ErrorAction Stop -Verbose:$false | Out-Null Add-Type -Path "$clientBinaryDirectory\Microsoft.AzureStack.SupportBridge.Management.Client.Contract.dll" -ErrorAction Stop -Verbose:$false | Out-Null Add-Type -Path "$clientBinaryDirectory\Microsoft.AzureStack.SupportBridge.Management.Client.dll" -ErrorAction Stop -Verbose:$false | Out-Null Add-Type -Path "$clientBinaryDirectory\Microsoft.AzureStack.SupportBridge.Mgmt.Client.Models.dll" -ErrorAction Stop -Verbose:$false | Out-Null Add-Type -Path "$clientBinaryDirectory\Microsoft.AzureStack.Common.Infrastructure.ServiceRegistry.ServiceTypeRegistry.dll" -ErrorAction Stop -Verbose:$false | Out-Null function Create-SupportControllerClientWithApplicationGateway { [CmdletBinding()] Param( [Parameter(Mandatory = $false, ValueFromPipeline = $true)] [string] $Endpoint = 'https://ASAppGateway:4443' ) # Create Instance of Support Controller Client Object. [System.TimeSpan] $defaultDelay = [System.TimeSpan]::FromSeconds(5) [System.TimeSpan] $maxRetryTime = [System.TimeSpan]::FromSeconds(30) #Create Instance with gateway resolver. $subrControllerClient = [Microsoft.AzureStack.SupportBridge.LogCollector.Client.LogCollectorClientFactory]::CreateGatewayInstance($Endpoint, $defaultDelay, $maxRetryTime) return $subrControllerClient } # This works while reaching out to SUBR from other VM's where SF is installed and SDN is working # as this requires VIP name resolution. function Create-SupportControllerClientWithServiceResolver { [CmdletBinding()] Param([string[]]$clientConnectionEndpoints = 'ASSRSFClient:19000' ) #Bugfix: Bug 3140655: Accessing ERCS VM Fails With "Unable to load DLL 'FabricClient.dll'" $env:Path = [System.Environment]::GetEnvironmentVariable("Path", "Machine") Add-Type -Path "$clientBinaryDirectory\Microsoft.AzureStack.Common.Infrastructure.ServiceDiscovery.ServiceFabric.Contract.dll" -ErrorAction Stop -Verbose:$false | Out-Null Add-Type -Path "$clientBinaryDirectory\Microsoft.AzureStack.Common.Infrastructure.ServiceDiscovery.ServiceFabric.dll" -ErrorAction Stop -Verbose:$false | Out-Null Add-Type -Path "$clientBinaryDirectory\Microsoft.AzureStack.Common.Infrastructure.ServiceRegistry.ServiceFabricTypeRegistry.dll" -ErrorAction Stop -Verbose:$false | Out-Null $resolver = [Microsoft.AzureStack.Common.Infrastructure.ServiceDiscovery.ServiceFabric.ServiceResolverFactory]::CreateServiceResolver( [Microsoft.AzureStack.Common.Infrastructure.ServiceRegistry.ServiceFabricTypeRegistry.ServiceFabricTypes]::TypeMap, $clientConnectionEndpoints) # Create Instance of Support Controller Client Object. [System.TimeSpan] $defaultDelay = [System.TimeSpan]::FromSeconds(5) [System.TimeSpan] $maxRetryTime = [System.TimeSpan]::FromSeconds(60) #Create Instance with gateway resolver. $subrControllerClient = [Microsoft.AzureStack.SupportBridge.LogCollector.Client.LogCollectorClientFactory]::CreateInstanceUsingServiceResolver($resolver, $defaultDelay, $maxRetryTime) return $subrControllerClient } # This works when service is running as WinSvC locally on the node. function Create-LogCollectorClienttWithLocalServiceResolver { $serviceId = [System.Guid]"754dbc04-8f91-4cb6-a10f-899dac573fa0" $serviceUri = [System.Uri]'http://localhost:7345' $dict = New-Object "System.Collections.Generic.Dictionary``2[System.Guid,System.Uri]" $dict.Add($serviceId, $serviceUri) # Create Instance of Support Controller Client Object. [System.TimeSpan] $defaultDelay = [System.TimeSpan]::FromSeconds(5) [System.TimeSpan] $maxRetryTime = [System.TimeSpan]::FromSeconds(60) Add-Type -Path "$clientBinaryDirectory\Microsoft.AzureStack.Common.Infrastructure.ServiceDiscovery.WinSvc.dll" -ErrorAction Stop -Verbose:$false | Out-Null $resolver = New-Object -TypeName 'Microsoft.AzureStack.Common.Infrastructure.ServiceDiscovery.WinSvc.ServiceResolver' -ArgumentList @($dict) #Create Instance with gateway resolver. $subrControllerLogCollectorClient = [Microsoft.AzureStack.SupportBridge.LogCollector.Client.LogCollectorClientFactory]::CreateInstanceUsingServiceResolver($resolver, $defaultDelay, $maxRetryTime, $null, $true) return $subrControllerLogCollectorClient } # This works while reaching out to SUBR from other VM's where SF is installed and SDN is working # as this requires VIP name resolution. function Create-SupportControllerMgmtClientWithServiceResolver { [CmdletBinding()] Param([string[]]$clientConnectionEndpoints = 'ASSRSFClient:19000' ) $isASZ = Is-ASZ if ($isASZ){ return Create-SupportControllerMgmtClientWithLocalServiceResolver -skipSPNAuth $true; } $ProviderName = [system.environment]::GetEnvironmentVariable("AzureStackProviderName",[System.EnvironmentVariableTarget]::Machine) if (($ProviderName -ne $null) -and ($ProviderName -ieq "Microsoft.HCI")){ return Create-SupportControllerMgmtClientWithLocalServiceResolver -skipSPNAuth $true; } #Bugfix: Bug 3140655: Accessing ERCS VM Fails With "Unable to load DLL 'FabricClient.dll'" $env:Path = [System.Environment]::GetEnvironmentVariable("Path", "Machine") Add-Type -Path "$clientBinaryDirectory\Microsoft.AzureStack.Common.Infrastructure.ServiceDiscovery.ServiceFabric.Contract.dll" -ErrorAction Stop -Verbose:$false | Out-Null Add-Type -Path "$clientBinaryDirectory\Microsoft.AzureStack.Common.Infrastructure.ServiceDiscovery.ServiceFabric.dll" -ErrorAction Stop -Verbose:$false | Out-Null Add-Type -Path "$clientBinaryDirectory\Microsoft.AzureStack.Common.Infrastructure.ServiceRegistry.ServiceFabricTypeRegistry.dll" -ErrorAction Stop -Verbose:$false | Out-Null $resolver = [Microsoft.AzureStack.Common.Infrastructure.ServiceDiscovery.ServiceFabric.ServiceResolverFactory]::CreateServiceResolver( [Microsoft.AzureStack.Common.Infrastructure.ServiceRegistry.ServiceFabricTypeRegistry.ServiceFabricTypes]::TypeMap, $clientConnectionEndpoints) # Create Instance of Support Controller Client Object. [System.TimeSpan] $defaultDelay = [System.TimeSpan]::FromSeconds(5) [System.TimeSpan] $maxRetryTime = [System.TimeSpan]::FromSeconds(60) #Create Instance with gateway resolver. $subrControllerMgmtClient = [Microsoft.AzureStack.SupportBridge.Management.Client.ManagementClientFactory]::CreateInstanceUsingServiceResolver($resolver, $defaultDelay, $maxRetryTime) return $subrControllerMgmtClient } # This works when service is running as WinSvC locally on the node. function Create-SupportControllerMgmtClientWithLocalServiceResolver { [CmdletBinding()] Param([bool]$skipSPNAuth = $false ) $serviceId = [System.Guid]"ea126685-c89e-4294-959f-bba6bf75b4aa" $serviceUri = [System.Uri]'http://localhost:80/diagnostics' $dict = New-Object "System.Collections.Generic.Dictionary``2[System.Guid,System.Uri]" $dict.Add($serviceId, $serviceUri) # Create Instance of Support Controller Client Object. [System.TimeSpan] $defaultDelay = [System.TimeSpan]::FromSeconds(5) [System.TimeSpan] $maxRetryTime = [System.TimeSpan]::FromSeconds(60) Add-Type -Path "$clientBinaryDirectory\Microsoft.AzureStack.Common.Infrastructure.ServiceDiscovery.WinSvc.dll" -ErrorAction Stop -Verbose:$false | Out-Null $resolver = New-Object -TypeName 'Microsoft.AzureStack.Common.Infrastructure.ServiceDiscovery.WinSvc.ServiceResolver' -ArgumentList @(,$dict) #Create Instance with gateway resolver. $subrControllerMgmtClient = [Microsoft.AzureStack.SupportBridge.Management.Client.ManagementClientFactory]::CreateInstanceUsingServiceResolver($resolver, $defaultDelay, $maxRetryTime, $null, $skipSPNAuth) return $subrControllerMgmtClient } <# .SYNOPSIS Sends Azure Stack Diagnostic Logs to Microsoft. .DESCRIPTION Makes a request to Support Bridge Service to upload diagnostic logs for stamp. .PARAMETER FilterByRole Optional. Filter by Infrastructure Role type. .PARAMETER FilterByResourceProvider Optional. Filter by Value-add resource provider type. .PARAMETER FromDate Optional. Start time to use for logs to collect. .PARAMETER ToDate Optional. End time to use for logs to collect. .EXAMPLE The example below sends last 4 hour logs to Microsoft. PS C:\> Send-AzureStackDiagnosticLog .EXAMPLE The example below sends last 4 hour Support Service and WAS logs to Microsoft. PS C:\> Send-AzureStackDiagnosticLog -FilterByRole SupportBridgeController,WAS .NOTES #> function Send-AzureStackDiagnosticLog { Param( [Parameter(Mandatory=$false)] [string[]] $FilterByRole, [Parameter(Mandatory=$false)] [string[]] $FilterByResourceProvider, [Parameter(Mandatory=$false)] [nullable[DateTime]] $FromDate, [Parameter(Mandatory=$false)] [nullable[DateTime]] $ToDate ) if ($FromDate -eq $null) { $FromDate = (Get-Date).AddHours(-4) Write-Host "FromDate parameter not specified. Setting to default value $FromDate" } if ($ToDate -eq $null) { $ToDate = Get-Date Write-Host "ToDate parameter not specified. Setting to default value $ToDate" } $logCollectionModel = Create-LogCollectionModel -FilterByRole $FilterByRole -FilterByResourceProvider $FilterByResourceProvider -FromDate $FromDate -ToDate $ToDate $subrControllerClient = Create-SupportControllerClientWithServiceResolver $collectionTask = $subrControllerClient.SubmitOnDemandCollectionJob("default", $logCollectionModel, [System.Threading.CancellationToken]::None) $collectionTask.Wait() $operationId = $collectionTask.Result Write-Host "Successfully submitted on-demand. Operation tracking Id: $operationId" [Microsoft.AzureStack.Common.Operations.OperationState]$runningState = [Microsoft.AzureStack.Common.Operations.OperationState]::Running [Microsoft.AzureStack.Common.Operations.OperationState]$currentState = Get-LogCollectionStatus -SubrControllerClient $subrControllerClient -OperationId $operationId Write-Host "Current log collection status: $currentState" while ($currentState -eq $runningState) { Write-Host "Waiting for log collection to complete..." Start-Sleep -s 120 $currentState = Get-LogCollectionStatus -SubrControllerClient $subrControllerClient -OperationId $operationId } Write-Host "Log collection ended with status: $currentState" } function Send-DiagnosticData { [CmdletBinding(DefaultParametersetName='LogCollectionAndParsing')] Param( [Parameter(Mandatory=$false, ParameterSetName='SaveToPath')] [Parameter(Mandatory=$false, ParameterSetName='LogCollectionAndParsing')] [string[]] $FilterByRole, [Parameter(Mandatory=$false, ParameterSetName='SaveToPath')] [Parameter(Mandatory=$false, ParameterSetName='LogCollectionAndParsing')] [nullable[DateTime]] $FromDate, [Parameter(Mandatory=$false, ParameterSetName='SaveToPath')] [Parameter(Mandatory=$false, ParameterSetName='LogCollectionAndParsing')] [nullable[DateTime]] $ToDate, [Parameter(Mandatory=$false, ParameterSetName='SaveToPath')] [Parameter(Mandatory=$false, ParameterSetName='LogCollectionAndParsing')] [bool] $CollectSddc = $true, [Parameter(Mandatory=$false, ParameterSetName='SaveToPath')] [string] $SaveToPath, [Parameter(Mandatory=$false, ParameterSetName='SaveToPath')] [Parameter(Mandatory=$false, ParameterSetName='NoLogCollection')] [Parameter(Mandatory=$false, ParameterSetName='LogCollectionAndParsing')] [ValidateNotNullOrEmpty()] [string] $SupplementaryLogs, [Parameter(Mandatory=$false, ParameterSetName='SaveToPath')] [Parameter(Mandatory=$false, ParameterSetName='NoLogCollection')] [ValidateNotNullOrEmpty()] [PSCredential] $ShareCredential, [Parameter(Mandatory=$false, ParameterSetName='NoLogCollection')] [switch] $NoLogCollection, [Parameter(Mandatory=$false)] [switch] $BypassObsAgent, [Parameter(Mandatory=$false, ParameterSetName='SaveToPath')] [switch] $ToSMBShare, [Parameter(Mandatory=$false, ParameterSetName='NoLogCollection')] [switch] $FromSMBShare, [Parameter(Mandatory=$false, ParameterSetName='NoLogCollection')] [Parameter(Mandatory=$false, ParameterSetName='SaveToPath')] [string] $SharePath, [Parameter(Mandatory=$false)] [switch] $SkipTestObservability ) if ($FromDate -eq $null) { $FromDate = (Get-Date).AddHours(-1) Write-Host "FromDate parameter not specified. Setting to default value $FromDate" } else { Write-Host "FromDate is $FromDate" } if ($ToDate -eq $null) { $ToDate = Get-Date Write-Host "ToDate parameter not specified. Setting to default value $ToDate" } else { Write-Host "ToDate is $ToDate" } Write-Host "The FromDate is in $($FromDate.Kind) and the ToDate is in $($ToDate.Kind)" Write-Host "The local time zone is $(Get-Timezone)" $FromDate = Convert-ToUtc $FromDate $ToDate = Convert-ToUtc $ToDate Write-Host "FromDate in UTC is now $FromDate. ToDate in UTC is now $ToDate" $CurrentDateTime = (Get-Date).ToUniversalTime() if ($ToDate -gt $CurrentDateTime.addMinutes(5)) { $OldToDate = $ToDate $ToDate = $CurrentDateTime Write-Warning "Log Collection To Date cannot be greater than current datetime of $CurrentDateTime. Changed Log Collection ToDate from $OldToDate to $ToDate" } if ($FromDate -gt $CurrentDateTime) { Write-Host "Log Collection From Date cannot be greater than current datetime of $CurrentDateTime. Aborting log collection." return } if ($ToDate -le $FromDate) { Write-Host "ToDate cannot be less than or equal to FromDate. Aborting log collection." return } # Warning if log collection is more than 24 hours in time range $timeDifference = New-TimeSpan -Start $fromDate -End $toDate # set to 24 hours + 2 minutes, in case time range is slightly above 24 hours $maxMinutes = 24 * 60 + 2 if ($timeDifference.TotalMinutes -gt $maxMinutes) { Write-Warning "We advise limiting log collection to no more than 24 hours." } # To make backwards compatible, accept parameters -ToSMBShare, -SMBShare, and SharePath if ($SharePath) { if ($ToSMBShare) { if ($SaveToPath) { Write-Host "Cannot use both SharePath and SaveToPath parameters. Aborting log collection." return } else { $SaveToPath = $SharePath Write-Warning "Note: ToSMBShare and SharePath parameters are deprecated. Use SaveToPath parameter instead of SharePath parameter going forward." } } elseif ($FromSMBShare) { if ($SupplementaryLogs) { Write-Host "Cannot use both SharePath and SupplementaryLogs parameters. Aborting log collection." return } else { $SupplementaryLogs = $SharePath $NoLogCollection = $true Write-Warning "Note: FromSMBShare and SharePath parameters are deprecated. Use NoLogCollection parameter instead of FromSMBShare parameter, and SupplementaryLogs parameter instead of SharePath parameter going forward." } } else { Write-Host "SharePath parameter must be used with either ToSMBShare or FromSMBShare. Aborting log collection." return } } # After checking if sharepath exists and reassigning as needed, need to enforce required parameters depending on parameter Set $parameterSetName = $PSCmdlet.ParameterSetName if ($parameterSetName -ieq "SaveToPath") { if ([string]::IsNullOrEmpty($SaveToPath)) { Write-Host "SaveToPath parameter cannot be null or empty when using parameter set $parameterSetName. Aborting log collection." return } } elseif ($parameterSetName -ieq "NoLogCollection") { if ([string]::IsNullOrEmpty($SupplementaryLogs)) { Write-Host "SupplementaryLogs parameter cannot be null or empty when using parameter set $parameterSetName. Aborting log collection." return } } $correlationId = [guid]::NewGuid() Write-Host "The correlation Id is $correlationId. This is used to query for this log collection in the diagnostic pipeline." if (!$SaveToPath) { # First need to check if GMA is running. If not, stop log collection. if (!(Is-GMARunning)) { Write-Host "GMA process for diagnostic pipeline was not found. Aborting log collection." return } try { $pathToDiagnosticJson = "$env:SystemDrive\Gmacache\JsonDropLocation\AEODiagnostics.json" $diagnosticsJson = Get-Content -Raw $pathToDiagnosticJson | ConvertFrom-json $diagnosticInfo = [ordered]@{ AEORegion = $diagnosticsJson.ConstantVariables.MONITORING_AEO_REGION AEODeviceARMResourceUri = $diagnosticsJson.ConstantVariables.MONITORING_AEO_DEVICE_ARM_RESOURCE_URI AEOClusterNodeArcResourceUri = $diagnosticsJson.ConstantVariables.MONITORING_AEO_NODE_ARC_RESOURCE_URI CorrelationId = $correlationId } Write-Host "Provide the below information to the customer support engineer working on your case." -Verbose $diagnosticInfo.GetEnumerator() | ForEach-Object { $message = ' {0}: {1}' -f $_.key, $_.value Write-Host $message } } catch { Write-Host "Could not get AEODeviceARMResourceUri, AEOClusterNodeArcResourceUri, or AEORegion. Error: $_" } } # If passed in SupplementaryLogs and parameter set name is SaveToPath or LogCollectionAndParsing, add warning if it doesn't exist. # If NoLogCollection parameter set, should be failure because there's nothing that can be collected and parsed. if (![string]::IsNullOrEmpty($SupplementaryLogs) ) { $validPath = Test-SupplementaryLogsPath -SupplementaryLogs $SupplementaryLogs -ShareCredential $ShareCredential -ParameterSetName $ParameterSetName $supplPathWarning = "Cannot find path for supplementary logs $SupplementaryLogs, or path is not a folder." if (!$validPath) { if ($parameterSetName -ieq "NoLogCollection") { Write-Host "$supplPathWarning Aborting log collection." return } else { Write-Warning $supplPathWarning Write-Host "Continuing with collecting role logs and sddc logs" } } } # Parameter sets SaveToPath and NoLogCollection should be a local log collection only. if ($NoLogCollection -or $SaveToPath) { Write-Host "Bypassing the observability agent, as parameter set $parameterSetName is being used" $BypassObsAgent = $true } if (-not $BypassObsAgent -and (Is-ObservabilityAgentRunning)) { Write-Host "Observability Agent is running." Write-Host "This log collection will attempt to collect logs from all hosts." $logCollectionModel = Create-LogCollectionModel -FilterByRole $FilterByRole -FromDate $FromDate -ToDate $ToDate ` -CollectSddc $CollectSddc -SupplementaryLogs $SupplementaryLogs -CorrelationId $correlationId -SkipTestObservability $SkipTestObservability.IsPresent $subrControllerClient = Create-LogCollectorClienttWithLocalServiceResolver $collectionTask = $subrControllerClient.SubmitOnDemandCollectionJob("default", $logCollectionModel, [System.Threading.CancellationToken]::None) $collectionTask.Wait() $operationId = $collectionTask.Result Write-Host "Successfully submitted on-demand. Log collection Job Id: $operationId. This is used to track the log collection with Get-LogCollectionHistory." [Microsoft.AzureStack.Common.Operations.OperationState]$runningState = [Microsoft.AzureStack.Common.Operations.OperationState]::Running [Microsoft.AzureStack.Common.Operations.OperationState]$currentState = Get-LogCollectionStatus -SubrControllerClient $subrControllerClient -OperationId $operationId Write-Host "Current log collection status: $currentState" while ($currentState -eq $runningState) { Write-Host "Waiting for log collection to complete..." Start-Sleep -s 120 $currentState = Get-LogCollectionStatus -SubrControllerClient $subrControllerClient -OperationId $operationId } Write-Host "Log Collection is Complete." # Figure out if any data parsed. $records = Get-LogCollectionHistory $count = ($records | Measure-Object).Count if ($count -eq 1) { $filesParsed = $records.UploadNumberOfFiles } else { $record = $records | Where-Object {$_.LogCollectionId -ieq $operationId} $filesParsed = $record.UploadNumberOfFiles } if ($filesParsed -gt 0) { Write-Host " Thank you for sending diagnostic logs to Microsoft. In the event that further logs are deemed necessary, Microsoft Customer Support will communicate with you accordingly. " } else { Write-Host "Log collection failed." } } else { Write-Host " Bypassing the observability agent and collecting logs directly from LogOrchestrator. There will be no record of this log collection in the log collection history. Only collecting logs from the current host, $env:COMPUTERNAME. " # Call Get-DiagnosticLog with the right parameters (including filtering for local node) # We are running in local mode, which means collect logs only for this node. $filterByNode = @($env:COMPUTERNAME) $getDiagLogParams = @{ TracingContext = $correlationId BypassedObsAgent = $true SupplementaryLogs = $SupplementaryLogs SkipTestObservability = $SkipTestObservability.IsPresent } # Don't pass in parameters used in collecting logs if we are using NoLogCollection parameter set, because # in that case, we skip log collection. if ($parameterSetName -ne "NoLogCollection") { $getDiagLogParams.Add("IncludeGetSDDCLogs", $CollectSddc) $getDiagLogParams.Add("FilterByRole", $FilterByRole) $getDiagLogParams.Add("FilterByNode", $filterByNode) $getDiagLogParams.Add("LogCollectionStartTimeUTC", $FromDate) $getDiagLogParams.Add("LogCollectionEndTimeUTC", $ToDate) $getDiagLogParams.Add("LogCollectionJobType", "OnDemand") $getDiagLogParams.Add("LocalMode", $true) } if ($parameterSetName -ieq "SaveToPath") { $getDiagLogParams.Add("SaveToPath", $SaveToPath) } if ($parameterSetName -ieq "NoLogCollection") { $getDiagLogParams.Add("NoLogCollection", $NoLogCollection) } if ($ShareCredential) { $getDiagLogParams.Add("ShareCredential", $ShareCredential) } Write-Host "Calling Get-DiagnosticLog." $diagnosticLogResults = Get-DiagnosticLog @getDiagLogParams Write-Host "Get-DiagnosticLog is complete." if ($parameterSetName -ne "SaveToPath") { $filesParsed = $diagnosticLogResults.GenevaConnector_ParsedFileCount if ($filesParsed -gt 0) { Write-Host "Thank you for sending diagnostic logs to Microsoft. In the event that further logs are deemed necessary, Microsoft Customer Support will communicate with you accordingly." } else { Write-Host "Log collection failed." } } } } <# .SYNOPSIS Gets the log colllection history. .DESCRIPTION Gets history of logs collected on the stamp. .PARAMETER MaxRecords Optional. Limit the number of records to display. By default is set to 10. If lower or equal to 0, return the default .PARAMETER AllRecords Optional. Defaults to false. Indicates whether to display all records. Ignores the value of MaxRecords .EXAMPLE PS C:\> Get-LogCollectionHistory .NOTES #> function Get-LogCollectionHistory { Param( [Parameter(Mandatory=$false)] [nullable[DateTime]] $FromDate = (Get-Date).AddDays(-7), [Parameter(Mandatory=$false)] [int] $MaxRecords = 10, [Parameter(Mandatory=$false)] [switch] $AllRecords ) if ($MaxRecords -le 0){ $MaxRecords = 10 } $logCollectionTable = $null $isASZ = Is-ASZ $subrControllerClient = $null if ($isASZ) { $subrControllerClient = Create-LogCollectorClienttWithLocalServiceResolver } else { $subrControllerClient = Create-SupportControllerClientWithServiceResolver } $fromDateString = $FromDate.ToString("MMddyyyyHHmmss") $getLogHistoryTask = $subrControllerClient.GetLogCollectionHistory([System.Threading.CancellationToken]::None, $fromDateString) $getLogHistoryTask.Wait() $logCollectionRecords = $getLogHistoryTask.Result $logCollectionData = [object[]]::new($logcollectionRecords.Count) $index = 0 foreach ($logcollectionRecord in $logCollectionRecords) { $newRecord = [ordered]@{ TimeCollected = $logcollectionRecord.CollectionTime Status = $logcollectionRecord.State LogCollectionId = $logCollectionRecord.OperationId CollectionFromDate = $logcollectionRecord.FromDate CollectionToDate = $logcollectionRecord.ToDate Type = $logcollectionRecord.JobType LogUploadSizeMb = $logcollectionRecord.UploadDetails.UploadSizeInMb UploadNumberOfFiles = $logcollectionRecord.UploadDetails.UploadNumberOfFiles Directory = $logcollectionRecord.UploadDetails.UploadDirectory Location = $logcollectionRecord.LogLocation Error = $logcollectionRecord.Error.Message -replace "`r`n",'; ' "----------" = "---------------------------------------------------------" } $logCollectionData[$index++] = $newRecord } $logCollectionTable = $logCollectionData | Sort-Object { [DateTime]$_.TimeCollected.DateTime } -Descending if (-not $AllRecords) { $logCollectionTable = $logCollectionTable | Select-Object -First $MaxRecords } if ($logCollectionTable.count -eq 0) { Write-Host "No records in time range." } return $logCollectionTable } <# .SYNOPSIS Enables proactive log collection. .DESCRIPTION Enables proactive log collection. .EXAMPLE The example below enables proactive log collection. PS C:\> Enable-ProactiveLogCollection .NOTES #> function Enable-ProactiveLogCollection { $isASZ = Is-ASZ $subrControllerClient = $null if ($isASZ) { $subrControllerClient = Create-LogCollectorClienttWithLocalServiceResolver $enableProactiveTask = $subrControllerClient.EnableASZProactiveLogCollection([System.Threading.CancellationToken]::None) } else { $subrControllerClient = Create-SupportControllerClientWithServiceResolver $enableProactiveTask = $subrControllerClient.EnableLogCollection([System.Threading.CancellationToken]::None) } $enableProactiveTask.Wait() # check if enabled $enabled = Check-ProactiveEnabled -SubrControllerClient $subrControllerClient -IsASZ $isASZ if ($enabled) { return "enabled" } else { throw "Proactive log collection could not be enabled." } } <# .SYNOPSIS Disables proactive log collection. .DESCRIPTION Disables proactive log collection. .EXAMPLE The example below enables disables log collection. PS C:\> Disable-ProactiveLogCollection .NOTES #> function Disable-ProactiveLogCollection { $isASZ = Is-ASZ $subrControllerClient = $null if ($isASZ) { $subrControllerClient = Create-LogCollectorClienttWithLocalServiceResolver $disableProactiveTask = $subrControllerClient.DisableASZProactiveLogCollection([System.Threading.CancellationToken]::None) } else { $subrControllerClient = Create-SupportControllerClientWithServiceResolver $disableProactiveTask = $subrControllerClient.DisableLogCollection([System.Threading.CancellationToken]::None) } $disableProactiveTask.Wait() # check if enabled $enabled = Check-ProactiveEnabled -SubrControllerClient $subrControllerClient -IsASZ $IsASZ if ($enabled) { throw "Proactive log collection could not be disabled." } else { return "disabled" } } function Check-ProactiveEnabled { Param( [Parameter(Mandatory=$true)] [Microsoft.AzureStack.SupportBridge.LogCollector.Client.Contract.ILogCollectorClient] $SubrControllerClient, [Parameter(Mandatory=$true)] [bool] $IsASZ ) if ($IsASZ) { $logCollectionConfigTask = $SubrControllerClient.GetASZProactiveLogCollectionState([System.Threading.CancellationToken]::None) $logCollectionConfigTask.Wait() $proactiveStatus = $logCollectionConfigTask.Result return ($proactiveStatus -eq "enabled") } else { $logCollectionConfigTask = $SubrControllerClient.GetScheduledLogCollectionConfiguration([System.Threading.CancellationToken]::None) $logCollectionConfigTask.Wait() $logCollectionConfig = $logCollectionConfigTask.Result return $logCollectionConfig.Enabled } } <# .SYNOPSIS Gets the proactive log collection state. .DESCRIPTION Gets the proactive log collection state. .EXAMPLE The example below gets the proactive log collection state.. PS C:\> Get-ProactiveLogCollectionState .NOTES #> function Get-ProactiveLogCollectionState { $isASZ = Is-ASZ $subrControllerClient = $null if ($isASZ) { $subrControllerClient = Create-LogCollectorClienttWithLocalServiceResolver } else { $subrControllerClient = Create-SupportControllerClientWithServiceResolver } $enabled = Check-ProactiveEnabled -SubrControllerClient $subrControllerClient -IsASZ $IsASZ if ($enabled) { return "enabled" } else { return "disabled" } } <# .SYNOPSIS Gets Support Service configuration settings. .DESCRIPTION Support Service configuration settings. .PARAMETER IncludeRegistrationObjectId Optional. Requires internet connectivity. Retrieves registration identity object id. .EXAMPLE The example below gets registration details if stamp was registered or else null. PS C:\> Get-AzureStackSupportConfiguration .NOTES Requires Support VM to have internet connectivity. #> function Get-AzureStackSupportConfiguration { Param( [Parameter(Mandatory=$false)] [Switch] $IncludeRegistrationObjectId ) try { Write-Host "Fetching registration details from Support Service." $includePropertyName = $null if ($IncludeRegistrationObjectId) { $includePropertyName = "objectId" } $subrControllerClient = Create-SupportControllerClientWithServiceResolver $getRegistrationDetailsTask = $subrControllerClient.GetLogCollectionRegistrationDetails([System.Threading.CancellationToken]::None, $includePropertyName) $getRegistrationDetailsTask.Wait() [Microsoft.AzureStack.SupportBridge.LogCollector.Client.Models.LogCollectionRegistrationDetailsClientModelExt]$registrationDetails = $getRegistrationDetailsTask.Result if ($registrationDetails.RegistrationResourceId -eq $null) { Write-Error "Registration Details not found. This can happen either if Azure Stack is not registered or Disconnected." } else { Write-Host "Successfully fetched registration details." return ($registrationDetails | ConvertTo-Json -Depth 2) } } catch { echo $_.Exception|format-list -force Write-Error "Get-AzureStackSupportConfiguration failed with $_" } } <# .SYNOPSIS Enables Cloud connection. .DESCRIPTION Enables Cloud connection. .EXAMPLE The example below enables cloud connection. PS C:\> Enable-AzsCloudConnection .NOTES Requires Support VM to have internet connectivity. #> function Enable-AzsCloudConnection { try { Write-Host "Enabling cloud connection..." $subrControllerMgmtClient = Create-SupportControllerMgmtClientWithServiceResolver $enableMgmtTask = $subrControllerMgmtClient.EnableRemoteManagement([System.Threading.CancellationToken]::None) $enableMgmtTask.Wait() Write-Host "Successfully enabled Cloud connection." } catch { echo $_.Exception|format-list -force Write-Error "Enable-AzsCloudConnection failed with $_" } finally { } } <# .SYNOPSIS Disables Cloud connection. .DESCRIPTION Disables Cloud connection. .EXAMPLE The example below disables Cloud connection. PS C:\> Disable-AzsCloudConnection .NOTES Requires Support VM to have internet connectivity. #> function Disable-AzsCloudConnection { try { Write-Host "Disabling Cloud connection..." $subrControllerMgmtClient = Create-SupportControllerMgmtClientWithServiceResolver $enableMgmtTask = $subrControllerMgmtClient.DisableRemoteManagement([System.Threading.CancellationToken]::None) $enableMgmtTask.Wait() Write-Host "Successfully disabled cloud connection." } catch { echo $_.Exception|format-list -force Write-Error "Disable-AzsCloudConnection failed with $_" } finally { } } <# .SYNOPSIS Adds cloud action. .DESCRIPTION Adds cloud action. .EXAMPLE The example below to add cloud action. PS C:\> Add-AzsCloudAction -SubsystemName $name -Expiration $date -Comments "sample access" .NOTES Requires Support VM to have internet connectivity. #> function Add-AzsCloudAction { Param( [Parameter(Mandatory=$true)] [string] $SubsystemName, [Parameter(Mandatory=$false)] [nullable[DateTime]] $Expiration, [Parameter(Mandatory=$false)] [string] $Comments ) try { if ($Expiration -eq $null) { $Expiration = (Get-Date).AddDays(8) Write-Host "Expiration parameter not specified. Setting to default value $Expiration" } Write-Host "Adding Cloud Action..." Write-Host "Parameters - SubsystemName: $SubsystemName Expiration: $Expiration Comments: $Comments" $subrControllerMgmtClient = Create-SupportControllerMgmtClientWithServiceResolver $mgmtAccessModel = Create-ManagementAccessModel -SubsystemName $SubsystemName -Expiration $Expiration -Comments $Comments $addMgmtAccessTask = $subrControllerMgmtClient.AddorUpdateActionConsent($mgmtAccessModel, [System.Threading.CancellationToken]::None) $addMgmtAccessTask.Wait() [Microsoft.AzureStack.SupportBridge.Management.Client.Models.SubsystemAccessClientModel]$mgmtAccessDetails = $addMgmtAccessTask.Result if ($mgmtAccessDetails -eq $null -or $mgmtAccessDetails.SubsystemName -eq $null) { Write-Error "Cloud action could not be added. This can happen if Cloud connection is not enabled. Try Enabling Cloud connection and then run this again." } else { Write-Host "Successfully added Cloud action." Write-Output $mgmtAccessDetails } } catch { echo $_.Exception|format-list -force Write-Error "Add-AzsCloudAction failed with $_" } finally { } } <# .SYNOPSIS Removes Cloud action. .DESCRIPTION Removes Cloud action. .EXAMPLE The example below removes cloud action. PS C:\> Remove-AzsCloudAction SubsystemName $name .NOTES Requires Support VM to have internet connectivity. #> function Remove-AzsCloudAction { Param( [Parameter(Mandatory=$true)] [string] $SubsystemName ) try { Write-Host "Removing Cloud action..." Write-Host "Parameters - SubsystemName: $SubsystemName" $subrControllerMgmtClient = Create-SupportControllerMgmtClientWithServiceResolver $deleteMgmtAccessTask = $subrControllerMgmtClient.RemoveActionConsent($SubsystemName, [System.Threading.CancellationToken]::None) $deleteMgmtAccessTask.Wait() Write-Host "Successfully removed Cloud action." } catch { echo $_.Exception|format-list -force Write-Error "Remove-AzsCloudAction failed with $_" } finally { } } <# .SYNOPSIS Gets Cloud actions. .DESCRIPTION Gets Cloud actions. .EXAMPLE The example below Gets Cloud actions. PS C:\> Get-AzsCloudAction .NOTES Requires Support VM to have internet connectivity. #> function Get-AzsCloudAction { Param( [Parameter(Mandatory=$false)] [switch] $IncludeExpired ) try { Write-Host "Getting list of Cloud actions..." Write-Host "Parameters - IncludeExpired: $IncludeExpired" $subrControllerMgmtClient = Create-SupportControllerMgmtClientWithServiceResolver $getMgmtAccessTask = $subrControllerMgmtClient.GetActionConsent($IncludeExpired, [System.Threading.CancellationToken]::None) $getMgmtAccessTask.Wait() [Microsoft.AzureStack.SupportBridge.Management.Client.Models.SubsystemAccessClientModel[]]$mgmtAccessDetails = $getMgmtAccessTask.Result if ($mgmtAccessDetails -eq $null -or $mgmtAccessDetails.SubsystemName -eq $null) { Write-Host "Cloud actions list is empty. " } else { Write-Host "Successfully retrieved Cloud actions list." Write-Output $mgmtAccessDetails } } catch { echo $_.Exception|format-list -force Write-Error "Get-AzsCloudAction failed with $_" } finally { } } <# .SYNOPSIS Enables Remote Support. .DESCRIPTION Enables Remote Support allows authorized Microsoft Support users to remotely access the device for diagnostics or repair depending on the access level granted. .PARAMETER AccessLevel Controls the remote operations that can be performed. This can be either Diagnostics or DiagnosticsAndRepair. .PARAMETER ExpireInMinutes Optional. Defaults to 8 hours. .PARAMETER SasCredential Optional. If provided by Microsoft Support, uses it to open remote support connection. .PARAMETER AgreeToRemoteSupportConsent Optional. If set to true then records user consent as provided and proceeds without prompt. .EXAMPLE The example below enables remote support for diagnostics only for 1 day. After expiration no more remote access is allowed. PS C:\> Enable-RemoteSupport -AccessLevel Diagnostics -ExpireInMinutes 1440 .NOTES Requires Support VM to have stable internet connectivity. #> function Enable-RemoteSupport { Param( [Parameter(Mandatory=$true)] [ValidateSet("Diagnostics","DiagnosticsRepair")] [string] $AccessLevel, [Parameter(Mandatory=$false)] [int] $ExpireInMinutes = 480, [Parameter(Mandatory=$false)] [string] $SasCredential, [Parameter(Mandatory=$false)] [switch] $AgreeToRemoteSupportConsent ) try { if ($ExpireInMinutes -lt 60) { Write-Error "ExpireInMinutes cannot be less than 1 hour(60 minutes)." return; } if ($ExpireInMinutes -gt 20160) { Write-Error "ExpireInMinutes cannot exceed beyond 14 days(20160 minutes)." return; } if ($AgreeToRemoteSupportConsent -ne $true) { Write-Host "`r`n`r`nBy approving this request, the Microsoft support organization or the Azure engineering team supporting this feature ('Microsoft Support Engineer') will be given direct access to your device for troubleshooting purposes and/or resolving the technical issue described in the Microsoft support case. `r`n`r`nDuring a remote support session, a Microsoft Support Engineer may need to collect logs. By enabling remote support, you have agreed to a diagnostic logs collection by Microsoft Support Engineer to address a support case You also acknowledge and consent to the upload and retention of those logs in an Azure storage account managed and controlled by Microsoft. These logs may be accessed by Microsoft in the context of a support case and to improve the health of Azure Stack Hub. `r`n`r`nThe data will be used only to troubleshoot failures that are subject to a support ticket, and will not be used for marketing, advertising, or any other commercial purposes without your consent. The data may be retained for up to ninety (90) days and will be handled following our standard privacy practices (https://privacy.microsoft.com/en-US/). Any data previously collected with your consent will not be affected by the revocation of your permission." while(1) { Write-Host "`r`nProceed with enabling remote support?" Write-Host "[Y] Yes [N] No: " -ForegroundColor Yellow -NoNewline $confirmation = Read-Host if ($confirmation -ieq 'Y' -or $confirmation -ieq 'YES') { break; } elseif ($confirmation -ieq 'N' -or $confirmation -ieq 'NO') { return; } } } # Arc Extension registry key check $agentInstalltype = (Get-ItemProperty -Path 'HKLM:\SYSTEM\Software\Microsoft\AzureStack\Observability\RemoteSupport' -ErrorAction SilentlyContinue).InstallType $arcExtensionRootPath = (Get-ItemProperty -Path 'HKLM:\SYSTEM\Software\Microsoft\AzureStack\Observability\RemoteSupport' -ErrorAction SilentlyContinue).ExtensionRootPath $JeaConfigSource = "RegistrationModule" if($agentInstalltype -eq "ArcExtension"){ if($(Get-Service "Observability Remote Support Agent" -ErrorAction SilentlyContinue).Status -eq "Running"){ $scriptPath = Join-Path -Path $arcExtensionRootPath -ChildPath 'Common\ConfigureJeaEndpoints.ps1' $scriptArguments = " -Constants RemoteSupportConstants -JeaConfigSource $JeaConfigSource" Write-Host "JEA configuration script path and arguments are : $scriptPath $scriptArguments" Write-Host "Configuring JEA Endpoints from '$JeaConfigSource'" # Configure JEA, will overwrite if JEA is already installed by startup task if(Test-Path -Path $scriptPath){ Invoke-Expression -Command "& '$scriptPath' $scriptArguments" } } } $targetService = "PowerShell" Write-Host "`r`n`r`Enabling Remote Support for '$AccessLevel' expiring in '$ExpireInMinutes' minutes." $subrControllerMgmtClient = Create-SupportControllerMgmtClientWithServiceResolver if(![string]::IsNullOrEmpty($SasCredential)){ $mgmtAccessModel = Create-RemoteSupportAccessRequestModel -TargetService $targetService -AccessLevel $AccessLevel -ExpireInMinutes $ExpireInMinutes -SasCredential $SasCredential } else { $mgmtAccessModel = Create-RemoteSupportAccessRequestModel -TargetService $targetService -AccessLevel $AccessLevel -ExpireInMinutes $ExpireInMinutes } $addMgmtAccessTask = $subrControllerMgmtClient.AddorUpdateRemoteSupportAccess($mgmtAccessModel, [System.Threading.CancellationToken]::None) $addMgmtAccessTask.Wait() [Microsoft.AzureStack.SupportBridge.Management.Client.Models.RemoteSupportAccessResponseClientModel]$mgmtAccessDetails = $addMgmtAccessTask.Result if ($mgmtAccessDetails -eq $null) { Write-Error "Remote Support could not be Enabled." } else { Write-Host "Remote Support successfully Enabled." Write-Output $mgmtAccessDetails } } catch { echo $_.Exception|format-list -force Write-Error "Enable-RemoteSupport failed with $_" } finally { } } <# .SYNOPSIS Disables Remote Support. .DESCRIPTION Disable Remote Support revokes all access levels previously granted. Any existing support sessions will be terminated, and new sessions can no longer be established. .EXAMPLE The example below disables remote support. PS C:\> Disable-RemoteSupport .NOTES #> function Disable-RemoteSupport { try { Write-Host "Disabling Remote Support." $subrControllerMgmtClient = Create-SupportControllerMgmtClientWithServiceResolver $disableRemoteSupportMgmtTask = $subrControllerMgmtClient.RevokeRemoteSupportAccess([System.Threading.CancellationToken]::None) $disableRemoteSupportMgmtTask.Wait() Write-Host "Remote Support successfully Disabled." } catch { echo $_.Exception|format-list -force Write-Error "Disable-RemoteSupport failed with $_" } finally { } } <# .SYNOPSIS Gets Remote Support Access. .DESCRIPTION Gets remote support access. .PARAMETER IncludeExpired Optional. Defaults to false. Indicates whether to include past expired entries. .EXAMPLE The example below retrieves access level granted for remote support. The result will also include expired consents in the last 30 days. PS C:\> List-RemoteSupportAccess -IncludeExpired $true .NOTES #> function Get-RemoteSupportAccess { Param( [Parameter(Mandatory=$false)] [switch] $IncludeExpired ) try { Write-Host "Retrieving Remote Support access. IncludeExpired is set to '$IncludeExpired'" $subrControllerMgmtClient = Create-SupportControllerMgmtClientWithServiceResolver $listRemoteSupportAccessMgmtTask = $subrControllerMgmtClient.ListRemoteSupportAccess($IncludeExpired, [System.Threading.CancellationToken]::None) $listRemoteSupportAccessMgmtTask.Wait() [Microsoft.AzureStack.SupportBridge.Management.Client.Models.RemoteSupportAccessResponseClientModel[]]$mgmtAccessDetails = $listRemoteSupportAccessMgmtTask.Result if ($mgmtAccessDetails -eq $null -or $mgmtAccessDetails.Length -eq 0) { Write-Host "No remote support access exists. " } else { Write-Output $mgmtAccessDetails } } catch { echo $_.Exception|format-list -force Write-Error "Get-RemoteSupportAccess failed with $_" } finally { } } <# .SYNOPSIS Gets Remote Support Session History Details. .DESCRIPTION Session history represents all remote accesses made by Microsoft Support for either Diagnostics or DiagnosticsRepair based on the Access Level granted. .PARAMETER SessionId Optional. Session Id to get details for a specific session. If omitted then lists all sessions starting from date 'FromDate'. .PARAMETER IncludeSessionTranscript Optional. Defaults to false. Indicates whether to include complete session transcript. Transcript provides details on all operations performed during the session. .PARAMETER FromDate Optional. Defaults to last 7 days. Indicates date from where to start listing sessions from until now. .PARAMETER MaxRecords Optional. Limit the number of records to display. By default is set to 10. If lower or equal to 0, return the default. Including a sessionId will always just return 1 record .PARAMETER AllRecords Optional. Defaults to false. Indicates whether to display all records. Ignores the value of MaxRecords .EXAMPLE The example below retrieves session history with transcript details for the specified session. PS C:\> Get-RemoteSupportSessionHistory -SessionId 467e3234-13f4-42f2-9422-81db248930fa -IncludeSessionTranscript $true .EXAMPLE The example below lists session history starting from last 7 days (default) to now. PS C:\> Get-RemoteSupportSessionHistory .NOTES #> function Get-RemoteSupportSessionHistory { Param( [Parameter(Mandatory=$false)] [string] $SessionId, [Parameter(Mandatory=$false)] [switch] $IncludeSessionTranscript, [Parameter(Mandatory=$false)] [DateTime] $FromDate = (Get-Date).AddDays(-7), [Parameter(Mandatory=$false)] [int] $MaxRecords = 10, [Parameter(Mandatory=$false)] [switch] $AllRecords ) try { if ($MaxRecords -le 0){ $MaxRecords = 10 } $subrControllerMgmtClient = Create-SupportControllerMgmtClientWithServiceResolver if($SessionId.Length -eq 0) { $currentTime = Get-Date; if ($FromDate -gt $currentTime) { Write-Error "From date cannot be a date in future." return; } [TimeSpan]$daysDifference = $currentTime - $FromDate; $totalDays = [math]::Round($daysDifference.TotalDays) if ($totalDays -gt 45) { Write-Error "From date cannot be past 45 days." return; } Write-Host "Listing Session History for last '$totalDays' days." $listRemoteSupportSessionMgmtTask = $subrControllerMgmtClient.ListRemoteSupportSession($FromDate, $false, [System.Threading.CancellationToken]::None) $listRemoteSupportSessionMgmtTask.Wait() [Microsoft.AzureStack.SupportBridge.Management.Client.Models.RemoteSupportSessionResponseClientModel[]]$listMgmtSessionDetails = $listRemoteSupportSessionMgmtTask.Result if (-not $AllRecords) { $listMgmtSessionDetails = $listMgmtSessionDetails | Sort-Object { [DateTime]$_.EndTime.DateTime } -Descending | Select-Object -First $MaxRecords } if ($listMgmtSessionDetails -eq $null -or $listMgmtSessionDetails.Length -eq 0) { Write-Host "No remote support session exists. " } else { Write-Output $listMgmtSessionDetails } } else { Write-Host "Retrieving Session History for Id '$SessionId'. Include session transcript was set to '$IncludeSessionTranscript'" $getRemoteSupportSessionMgmtTask = $subrControllerMgmtClient.GetRemoteSupportSession($SessionId, $IncludeSessionTranscript, [System.Threading.CancellationToken]::None) $getRemoteSupportSessionMgmtTask.Wait() [Microsoft.AzureStack.SupportBridge.Management.Client.Models.RemoteSupportSessionResponseClientModel]$getMgmtSessionDetails = $getRemoteSupportSessionMgmtTask.Result if ($getMgmtSessionDetails -eq $null) { Write-Host "No remote support session with id '$SessionId' exists." } else { Write-Output $getMgmtSessionDetails } } } catch { echo $_.Exception|format-list -force Write-Error "Get-RemoteSupportSessionHistory failed with $_" } finally { } } #Helper functions for creating remote management access objects. function Create-ManagementAccessModel { [CmdletBinding()] Param( [Parameter(Mandatory=$true)] [string] $SubsystemName, [Parameter(Mandatory=$false)] [DateTime] $Expiration, [Parameter(Mandatory=$false)] [string] $Comments ) $mgmtAccessModel = New-Object -TypeName 'Microsoft.AzureStack.SupportBridge.Management.Client.Models.SubsystemAccessClientModel' $mgmtAccessModel.SubsystemName = $SubsystemName $mgmtAccessModel.ExpiresAt = $Expiration $mgmtAccessModel.Comments = $Comments Write-Output $mgmtAccessModel } #Helper functions for creating client model objects. function Create-LogCollectionModel { [CmdletBinding()] Param( [Parameter(Mandatory=$false)] [string[]] $FilterByRole, [Parameter(Mandatory=$false)] [string[]] $FilterByResourceProvider, [Parameter(Mandatory=$true)] [DateTime] $FromDate, [Parameter(Mandatory=$true)] [DateTime] $ToDate, [Parameter(Mandatory=$false)] [bool] $CollectSddc = $false, [Parameter(Mandatory=$false)] [string] $SupplementaryLogs, [Parameter(Mandatory=$false)] [Guid] $CorrelationId = [System.Guid]::Empty, [Parameter(Mandatory=$false)] [bool] $SkipTestObservability = $false ) $logCollectionModel = New-Object -TypeName 'Microsoft.AzureStack.SupportBridge.LogCollector.Client.Models.LogCollectionJobClientModel' $logCollectionModel.FilterRoles = $FilterByRole $logCollectionModel.FilterResourceProviders = $FilterByResourceProvider $logCollectionModel.FromDate = $FromDate $logCollectionModel.ToDate = $ToDate $logCollectionModel.CollectSddc = $CollectSddc $logCollectionModel.SupplementaryLogs = $SupplementaryLogs $logCollectionModel.CorrelationId = $CorrelationId $logCollectionModel.SkipTestObservability = $SkipTestObservability Write-Output $logCollectionModel } function Get-LogCollectionStatus { [CmdletBinding()] Param( [Parameter(Mandatory=$true)] [Microsoft.AzureStack.SupportBridge.LogCollector.Client.Contract.ILogCollectorClient] $SubrControllerClient, [Parameter(Mandatory=$true)] [string] $OperationId ) $collectionStatusTask = $SubrControllerClient.GetLogCollectionJobOperationStatus($OperationId, [System.Threading.CancellationToken]::None) $collectionStatusTask.Wait() $collectionStatus = $collectionStatusTask.Result $collectionStatusRecord = $collectionStatus.StatusRecord [Microsoft.AzureStack.Common.Operations.OperationState]$collectionState = $collectionStatusRecord.State Write-Output $collectionState } #Helper functions for creating remote support access request object. function Create-RemoteSupportAccessRequestModel { [CmdletBinding()] Param( [Parameter(Mandatory=$true)] [string] $TargetService, [Parameter(Mandatory=$true)] [string] $AccessLevel, [Parameter(Mandatory=$true)] [int] $ExpireInMinutes, [Parameter(Mandatory=$false)] [string] $SasCredential ) $ExpiresAt=[system.datetimeoffset]::now.AddMinutes($ExpireInMinutes) $mgmtRemoteSupportReqAccessModel = New-Object -TypeName 'Microsoft.AzureStack.SupportBridge.Management.Client.Models.RemoteSupportAccessRequestClientModel' $mgmtRemoteSupportReqAccessModel.TargetService = $TargetService $mgmtRemoteSupportReqAccessModel.AccessLevel = $AccessLevel $mgmtRemoteSupportReqAccessModel.ExpiresAt = $ExpiresAt if(![String]::IsNullOrWhiteSpace($SasCredential)) { Write-Host "Using provided SAS credential to make remote support connection." $mgmtRemoteSupportReqAccessModel.SasCredential = $SasCredential } Write-Output $mgmtRemoteSupportReqAccessModel } function Is-ASZ { $path = "HKLM:\SOFTWARE\Microsoft\AzureStack" $name = "DeviceType" $keyNotFound = $false if (Test-Path $path) { $Key = Get-Item -LiteralPath $Path if ($null -ne $Key.GetValue($Name)) { $value = $Key.GetValue($Name) if ($value -eq "AzureEdge" -or $value -eq "HCI") { return $true } else { return $false } } else { $keyNotFound = $true } } else { $keyNotFound = $true } if ($keyNotFound) { # could be that key doesn't exist because Hub environment or because ASZ deployment failed before the key was created. # If failed in ASZ before key created, then cmdlets (get-logcollectionhistory, enable/disable proactive, #get-proactivelogcollectionstate) will fail anyway, because observability agent isn't up. Write-Host "DeviceType not found. Assuming DeviceType is not AzureEdge" return $false } } ##### Send-DiagnosticData Helper Functions ####### function Is-ObservabilityAgentRunning { try { $service = get-service -name "AzureStack Observability Agent" return ($service.Status -eq "Running") } catch { Write-Error "Could not get AzureStack Observability Agent service: $_" return $false } } function Is-GMARunning { # Note: If GMA is not present, the log collection will not fail. It will just delete # logs without parsing them. Thus, we should throw failure without trying to collect logs # if GMA is not running. $processes = (gwmi win32_process|?{$_.name -like "*MonAgentHost*"}).CommandLine $diagRunning = $false $pattern = '-serviceIdentity "DiagnosticsPROD#AzureEdgeObs.*Diag#\w+"' foreach ($process in $processes) { if ($process -match $pattern) { $diagRunning = $true } } return $diagRunning } function Connect-ToSharePath { [CmdletBinding()] param( [Parameter(Mandatory=$true)] [string] $SharePath, [Parameter(Mandatory=$true)] [PSCredential] $Credential ) $bstr = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($Credential.Password) $password = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($bstr) $drives = @('H', 'K', 'O', 'R', 'W') foreach ($driveLetter in $drives) { $drive = $driveLetter + ":" $null = net use $drive $SharePath /user:$($Credential.UserName) $password if($LASTEXITCODE -ne 0) { Continue } if (Test-Path $SharePath) { return $drive } } return $null } function Test-SupplementaryLogsPath { [CmdletBinding()] param( [Parameter(Mandatory=$true)] [string] $SupplementaryLogs, [Parameter(Mandatory=$false)] [PSCredential] $ShareCredential, [Parameter(Mandatory=$true)] [string] $ParameterSetName ) # If parameter set is NoLogCollection, ShareCredential is for SupplementaryLogs. # If parameter set is SaveToPath, the ShareCredential is for SaveToPath. # If parameter set is LogCollectionAndParsing, ShareCredential is not allowed. # Thus, only test mapping to SupplementaryLogs path using ShareCredential if parameter set is NoLogCollection. if ($ParameterSetName -ieq "NoLogCollection" -and $ShareCredential) { $drive = Connect-ToSharePath -SharePath $SupplementaryLogs -Credential $ShareCredential } # Split Test-Path <path> -PathType Container into two statements, because -ErrorAction SilentlyContinue # shows errors when it is all in one statement. if ((Test-Path $SupplementaryLogs -ErrorAction SilentlyContinue) -and (Test-Path $SupplementaryLogs -PathType Container)) { $validPath = $true } else { $validPath = $false } if ($null -ne $drive) { net use $drive /delete /yes } return $validPath } function Convert-ToUTC($date) { if ($date.Kind -eq [System.DateTimeKind]::Utc) { # Date is already in UTC, no need to convert return $date } else { # Convert to UTC return $date.ToUniversalTime() } } Set-Alias -Name Send-AzureStackDiagnosticLogs -Value Send-AzureStackDiagnosticLog $isASZ = Is-ASZ if ($isASZ) { Export-ModuleMember -Function Send-DiagnosticData Export-ModuleMember -function Get-LogCollectionHistory Export-ModuleMember -Function Enable-ProactiveLogCollection Export-ModuleMember -Function Disable-ProactiveLogCollection Export-ModuleMember -Function Get-ProactiveLogCollectionState Export-ModuleMember -Function Enable-RemoteSupport Export-ModuleMember -Function Disable-RemoteSupport Export-ModuleMember -Function Get-RemoteSupportAccess Export-ModuleMember -Function Get-RemoteSupportSessionHistory } else { # if returns false, could be because Hub or because ASZ failed early in deployment. # To Be safe, export everything. Export-ModuleMember -Function Send-AzureStackDiagnosticLog Export-ModuleMember -Function Send-DiagnosticData Export-ModuleMember -function Get-LogCollectionHistory Export-ModuleMember -Function Enable-ProactiveLogCollection Export-ModuleMember -Function Disable-ProactiveLogCollection Export-ModuleMember -Function Get-ProactiveLogCollectionState Export-ModuleMember -Function Get-AzureStackSupportConfiguration Export-ModuleMember -Function Enable-AzsCloudConnection Export-ModuleMember -Function Disable-AzsCloudConnection Export-ModuleMember -Function Add-AzsCloudAction Export-ModuleMember -Function Remove-AzsCloudAction Export-ModuleMember -Function Get-AzsCloudAction Export-ModuleMember -Function Enable-RemoteSupport Export-ModuleMember -Function Disable-RemoteSupport Export-ModuleMember -Function Get-RemoteSupportAccess Export-ModuleMember -Function Get-RemoteSupportSessionHistory } # SIG # Begin signature block # MIIoKQYJKoZIhvcNAQcCoIIoGjCCKBYCAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCAeVNCzuoJCz2Ij # mMZHRWPFmgVJhIoNyHCpo8SkFt6Q4aCCDXYwggX0MIID3KADAgECAhMzAAADrzBA # DkyjTQVBAAAAAAOvMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD # VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p # bmcgUENBIDIwMTEwHhcNMjMxMTE2MTkwOTAwWhcNMjQxMTE0MTkwOTAwWjB0MQsw # CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u # ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB # AQDOS8s1ra6f0YGtg0OhEaQa/t3Q+q1MEHhWJhqQVuO5amYXQpy8MDPNoJYk+FWA # hePP5LxwcSge5aen+f5Q6WNPd6EDxGzotvVpNi5ve0H97S3F7C/axDfKxyNh21MG # 0W8Sb0vxi/vorcLHOL9i+t2D6yvvDzLlEefUCbQV/zGCBjXGlYJcUj6RAzXyeNAN # xSpKXAGd7Fh+ocGHPPphcD9LQTOJgG7Y7aYztHqBLJiQQ4eAgZNU4ac6+8LnEGAL # go1ydC5BJEuJQjYKbNTy959HrKSu7LO3Ws0w8jw6pYdC1IMpdTkk2puTgY2PDNzB # tLM4evG7FYer3WX+8t1UMYNTAgMBAAGjggFzMIIBbzAfBgNVHSUEGDAWBgorBgEE # AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQURxxxNPIEPGSO8kqz+bgCAQWGXsEw # RQYDVR0RBD4wPKQ6MDgxHjAcBgNVBAsTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEW # MBQGA1UEBRMNMjMwMDEyKzUwMTgyNjAfBgNVHSMEGDAWgBRIbmTlUAXTgqoXNzci # tW2oynUClTBUBgNVHR8ETTBLMEmgR6BFhkNodHRwOi8vd3d3Lm1pY3Jvc29mdC5j # b20vcGtpb3BzL2NybC9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDctMDguY3JsMGEG # CCsGAQUFBwEBBFUwUzBRBggrBgEFBQcwAoZFaHR0cDovL3d3dy5taWNyb3NvZnQu # Y29tL3BraW9wcy9jZXJ0cy9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDctMDguY3J0 # MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggIBAISxFt/zR2frTFPB45Yd # mhZpB2nNJoOoi+qlgcTlnO4QwlYN1w/vYwbDy/oFJolD5r6FMJd0RGcgEM8q9TgQ # 2OC7gQEmhweVJ7yuKJlQBH7P7Pg5RiqgV3cSonJ+OM4kFHbP3gPLiyzssSQdRuPY # 1mIWoGg9i7Y4ZC8ST7WhpSyc0pns2XsUe1XsIjaUcGu7zd7gg97eCUiLRdVklPmp # XobH9CEAWakRUGNICYN2AgjhRTC4j3KJfqMkU04R6Toyh4/Toswm1uoDcGr5laYn # TfcX3u5WnJqJLhuPe8Uj9kGAOcyo0O1mNwDa+LhFEzB6CB32+wfJMumfr6degvLT # e8x55urQLeTjimBQgS49BSUkhFN7ois3cZyNpnrMca5AZaC7pLI72vuqSsSlLalG # OcZmPHZGYJqZ0BacN274OZ80Q8B11iNokns9Od348bMb5Z4fihxaBWebl8kWEi2O # PvQImOAeq3nt7UWJBzJYLAGEpfasaA3ZQgIcEXdD+uwo6ymMzDY6UamFOfYqYWXk # ntxDGu7ngD2ugKUuccYKJJRiiz+LAUcj90BVcSHRLQop9N8zoALr/1sJuwPrVAtx # HNEgSW+AKBqIxYWM4Ev32l6agSUAezLMbq5f3d8x9qzT031jMDT+sUAoCw0M5wVt # CUQcqINPuYjbS1WgJyZIiEkBMIIHejCCBWKgAwIBAgIKYQ6Q0gAAAAAAAzANBgkq # hkiG9w0BAQsFADCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24x # EDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlv # bjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5 # IDIwMTEwHhcNMTEwNzA4MjA1OTA5WhcNMjYwNzA4MjEwOTA5WjB+MQswCQYDVQQG # EwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwG # A1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSgwJgYDVQQDEx9NaWNyb3NvZnQg # Q29kZSBTaWduaW5nIFBDQSAyMDExMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC # CgKCAgEAq/D6chAcLq3YbqqCEE00uvK2WCGfQhsqa+laUKq4BjgaBEm6f8MMHt03 # a8YS2AvwOMKZBrDIOdUBFDFC04kNeWSHfpRgJGyvnkmc6Whe0t+bU7IKLMOv2akr # rnoJr9eWWcpgGgXpZnboMlImEi/nqwhQz7NEt13YxC4Ddato88tt8zpcoRb0Rrrg # OGSsbmQ1eKagYw8t00CT+OPeBw3VXHmlSSnnDb6gE3e+lD3v++MrWhAfTVYoonpy # 4BI6t0le2O3tQ5GD2Xuye4Yb2T6xjF3oiU+EGvKhL1nkkDstrjNYxbc+/jLTswM9 # sbKvkjh+0p2ALPVOVpEhNSXDOW5kf1O6nA+tGSOEy/S6A4aN91/w0FK/jJSHvMAh # dCVfGCi2zCcoOCWYOUo2z3yxkq4cI6epZuxhH2rhKEmdX4jiJV3TIUs+UsS1Vz8k # A/DRelsv1SPjcF0PUUZ3s/gA4bysAoJf28AVs70b1FVL5zmhD+kjSbwYuER8ReTB # w3J64HLnJN+/RpnF78IcV9uDjexNSTCnq47f7Fufr/zdsGbiwZeBe+3W7UvnSSmn # Eyimp31ngOaKYnhfsi+E11ecXL93KCjx7W3DKI8sj0A3T8HhhUSJxAlMxdSlQy90 # lfdu+HggWCwTXWCVmj5PM4TasIgX3p5O9JawvEagbJjS4NaIjAsCAwEAAaOCAe0w # ggHpMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBRIbmTlUAXTgqoXNzcitW2o # ynUClTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMCAYYwDwYD # VR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBRyLToCMZBDuRQFTuHqp8cx0SOJNDBa # BgNVHR8EUzBRME+gTaBLhklodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2Ny # bC9wcm9kdWN0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFfMDNfMjIuY3JsMF4GCCsG # AQUFBwEBBFIwUDBOBggrBgEFBQcwAoZCaHR0cDovL3d3dy5taWNyb3NvZnQuY29t # L3BraS9jZXJ0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFfMDNfMjIuY3J0MIGfBgNV # HSAEgZcwgZQwgZEGCSsGAQQBgjcuAzCBgzA/BggrBgEFBQcCARYzaHR0cDovL3d3 # dy5taWNyb3NvZnQuY29tL3BraW9wcy9kb2NzL3ByaW1hcnljcHMuaHRtMEAGCCsG # AQUFBwICMDQeMiAdAEwAZQBnAGEAbABfAHAAbwBsAGkAYwB5AF8AcwB0AGEAdABl # AG0AZQBuAHQALiAdMA0GCSqGSIb3DQEBCwUAA4ICAQBn8oalmOBUeRou09h0ZyKb # C5YR4WOSmUKWfdJ5DJDBZV8uLD74w3LRbYP+vj/oCso7v0epo/Np22O/IjWll11l # hJB9i0ZQVdgMknzSGksc8zxCi1LQsP1r4z4HLimb5j0bpdS1HXeUOeLpZMlEPXh6 # I/MTfaaQdION9MsmAkYqwooQu6SpBQyb7Wj6aC6VoCo/KmtYSWMfCWluWpiW5IP0 # wI/zRive/DvQvTXvbiWu5a8n7dDd8w6vmSiXmE0OPQvyCInWH8MyGOLwxS3OW560 # STkKxgrCxq2u5bLZ2xWIUUVYODJxJxp/sfQn+N4sOiBpmLJZiWhub6e3dMNABQam # ASooPoI/E01mC8CzTfXhj38cbxV9Rad25UAqZaPDXVJihsMdYzaXht/a8/jyFqGa # J+HNpZfQ7l1jQeNbB5yHPgZ3BtEGsXUfFL5hYbXw3MYbBL7fQccOKO7eZS/sl/ah # XJbYANahRr1Z85elCUtIEJmAH9AAKcWxm6U/RXceNcbSoqKfenoi+kiVH6v7RyOA # 9Z74v2u3S5fi63V4GuzqN5l5GEv/1rMjaHXmr/r8i+sLgOppO6/8MO0ETI7f33Vt # Y5E90Z1WTk+/gFcioXgRMiF670EKsT/7qMykXcGhiJtXcVZOSEXAQsmbdlsKgEhr # /Xmfwb1tbWrJUnMTDXpQzTGCGgkwghoFAgEBMIGVMH4xCzAJBgNVBAYTAlVTMRMw # EQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVN # aWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNp # Z25pbmcgUENBIDIwMTECEzMAAAOvMEAOTKNNBUEAAAAAA68wDQYJYIZIAWUDBAIB # BQCgga4wGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEO # MAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIMUC8Wl6gcJMRPVzftp/sDwC # YcYarF7upmA1Ip00c1CLMEIGCisGAQQBgjcCAQwxNDAyoBSAEgBNAGkAYwByAG8A # cwBvAGYAdKEagBhodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20wDQYJKoZIhvcNAQEB # BQAEggEAqAKl6PmHfiRh6SIobgr6e8H+9cfA7Na2+FDuvF0vxaD5cyZEMZFphEYe # iDp/TRrxO+E5IbtmgsIoW2UDUYLx5jc0xg/QJ3JnDcC36ixlGpwZTWPUC5K6nLA6 # 1NSdaDCfxajgWKb4iRKXCr6ptd0lC460GsKCezURHofCMyT7OZnvYfZ2s/PbKVPz # cx6JWUcPmFo98b12aVx21leelnBEu67Dr1UxycUcwDjxLd8rKTB/gXIG2X/zyI8j # qdmI2tdPki6N4otCEIRHuQxa1gHxrG69yYpXn8fDSPEWjUYSwDEPFiGhUTrPa0b0 # dPXSj/0rnl91TN2HL9OE0mJw+uVFJaGCF5MwghePBgorBgEEAYI3AwMBMYIXfzCC # F3sGCSqGSIb3DQEHAqCCF2wwghdoAgEDMQ8wDQYJYIZIAWUDBAIBBQAwggFRBgsq # hkiG9w0BCRABBKCCAUAEggE8MIIBOAIBAQYKKwYBBAGEWQoDATAxMA0GCWCGSAFl # AwQCAQUABCBlgXAL4gsxxqZO9C/SbdLkXayq64N8o5UcjBKMqhYT7AIGZogq6ZWW # GBIyMDI0MDcwOTA4NTQzNS4zMVowBIACAfSggdGkgc4wgcsxCzAJBgNVBAYTAlVT # MRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQK # ExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJTAjBgNVBAsTHE1pY3Jvc29mdCBBbWVy # aWNhIE9wZXJhdGlvbnMxJzAlBgNVBAsTHm5TaGllbGQgVFNTIEVTTjozNzAzLTA1 # RTAtRDk0NzElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2VydmljZaCC # EeowggcgMIIFCKADAgECAhMzAAAB6pokctVZP2FjAAEAAAHqMA0GCSqGSIb3DQEB # CwUAMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQH # EwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNV # BAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwMB4XDTIzMTIwNjE4NDUz # MFoXDTI1MDMwNTE4NDUzMFowgcsxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNo # aW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29y # cG9yYXRpb24xJTAjBgNVBAsTHE1pY3Jvc29mdCBBbWVyaWNhIE9wZXJhdGlvbnMx # JzAlBgNVBAsTHm5TaGllbGQgVFNTIEVTTjozNzAzLTA1RTAtRDk0NzElMCMGA1UE # AxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2VydmljZTCCAiIwDQYJKoZIhvcNAQEB # BQADggIPADCCAgoCggIBALULX/FIPyAH1fsu52ijatZvaSypoXrlC0mRtCmaxzob # huDkw6/pY/+4nhc4m8pf9zW3R6PihYGp0YPpVuNdfhPQp/KVO6WvMq2DGfFmHurW # 4PQPL/DkbQMkM9vqjFCvPq8xXZnfL1nGN9moGcN+oaif/hUMedmF1qzbay9ILkYf # LCxDYn3Qwzsvh5xjxOcsjzmRddNURJvT23Eva0cxisH4ocLLTx2zfpqfshw4Z9Ga # EdsWg9rmib1galUpLzF5PsQDBbtZtcv+Wjmn0pFEiMCWwEEcPVN0YG5ysYLdNBdJ # On2zsOOS+80W5RrQEqzPpSIIvEkZBJmF3aI4lMR8nV/FiTadjpIIqxX5Wa1XlqI/ # Nj+xagVjnjb7POsA+vh6Wu+v24HpyL8pyL/8Q4RFkRRME9cwT+Jr63yOtPbLe6DX # kxIJW6E6w2ua5kXBpEKtEQPTLPhX3CUxMYcglbnmI0zcc9UknX285K+sI/2WwRwT # BZkhDUULI86eQzV+zvzzR1qEBrlSY+oyTlYQrHMM9WnTzVflFDocZVTPpl2BDSNx # Pn0Qb4IoM9EPqbHyi/MilL+v/AQc8q3mQ6FiuPJAddz0ocpNZ9ekBWPVLKq3lfie # v4yl65u/438+NAQ+vSJgkONLMmuoguEGzmnK1vq/JHwdRUyn6YADiteM7Dja+Qd9 # AgMBAAGjggFJMIIBRTAdBgNVHQ4EFgQUK4FFJaJR5ukXQFTUxMhyiwVuWV4wHwYD # VR0jBBgwFoAUn6cVXQBeYl2D9OXSZacbUzUZ6XIwXwYDVR0fBFgwVjBUoFKgUIZO # aHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9jcmwvTWljcm9zb2Z0JTIw # VGltZS1TdGFtcCUyMFBDQSUyMDIwMTAoMSkuY3JsMGwGCCsGAQUFBwEBBGAwXjBc # BggrBgEFBQcwAoZQaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9jZXJ0 # cy9NaWNyb3NvZnQlMjBUaW1lLVN0YW1wJTIwUENBJTIwMjAxMCgxKS5jcnQwDAYD # VR0TAQH/BAIwADAWBgNVHSUBAf8EDDAKBggrBgEFBQcDCDAOBgNVHQ8BAf8EBAMC # B4AwDQYJKoZIhvcNAQELBQADggIBACiDrVZeP37+fFVtfcbfsqC/Kg0Ce67bDceh # ZmPcfRgJ5Ddv0pJlOFVOFbiIVwesqeEUwFtclfi5AjneQ5ZJpYJpXfELOelG3dzj # +BKfd287/UY/cwmSkl+CjnoKBL3Ms6I/fWR+alR0+p6RlviK8xHoug9vkc2WrRZs # GnMVu2xOM2tPJ+qpyoDBzqv30N/ZRBOoNrS/PCkDwLGICDYqVs/IzAE49yv2ElPy # walf9mEsOHXV1lxtQDNcejVEmitJJ+1Vr2EtafPEbMQZp89TAuagROKE4YuohCUK # m+v3geJqTQarTBjqV25RCOT+XFngTMDD9wYx6TwndB2I1Ly726NiHUHs0uvq3ciC # V9JwNXdt1VZ63WK1NSgpVEsiK9EPABPt1EfXcKrfaPYkbkFi79eK1ETxx3NomYNU # HNiGU+X1Be8L7qpHwjo0g3/33XhtOr9LiDoUXh/V2LFTETiqV9Q8yLEavQW3j9LQ # /h/CaGz5YdGfrY8HiPfMIeLEokKxGf0hHcTEFApB0yLlq6KoHrFAEANR/4XuFIpl # 9sDywVIWt4tKqG+P6pRAXzg1zG5rGlslZWmw7XwgvhBu3jkLP9AxrsSYwY2ftrww # ze5NA6VDLS7pz+OrXXWLUmoyNrJNx5Bk0wEwzkQxzkOvmbdPhsOP1ZM0uA/xIV7c # SpNpZUw5MIIHcTCCBVmgAwIBAgITMwAAABXF52ueAptJmQAAAAAAFTANBgkqhkiG # 9w0BAQsFADCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAO # BgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEy # MDAGA1UEAxMpTWljcm9zb2Z0IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIw # MTAwHhcNMjEwOTMwMTgyMjI1WhcNMzAwOTMwMTgzMjI1WjB8MQswCQYDVQQGEwJV # UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UE # ChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGlt # ZS1TdGFtcCBQQ0EgMjAxMDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB # AOThpkzntHIhC3miy9ckeb0O1YLT/e6cBwfSqWxOdcjKNVf2AX9sSuDivbk+F2Az # /1xPx2b3lVNxWuJ+Slr+uDZnhUYjDLWNE893MsAQGOhgfWpSg0S3po5GawcU88V2 # 9YZQ3MFEyHFcUTE3oAo4bo3t1w/YJlN8OWECesSq/XJprx2rrPY2vjUmZNqYO7oa # ezOtgFt+jBAcnVL+tuhiJdxqD89d9P6OU8/W7IVWTe/dvI2k45GPsjksUZzpcGkN # yjYtcI4xyDUoveO0hyTD4MmPfrVUj9z6BVWYbWg7mka97aSueik3rMvrg0XnRm7K # MtXAhjBcTyziYrLNueKNiOSWrAFKu75xqRdbZ2De+JKRHh09/SDPc31BmkZ1zcRf # NN0Sidb9pSB9fvzZnkXftnIv231fgLrbqn427DZM9ituqBJR6L8FA6PRc6ZNN3SU # HDSCD/AQ8rdHGO2n6Jl8P0zbr17C89XYcz1DTsEzOUyOArxCaC4Q6oRRRuLRvWoY # WmEBc8pnol7XKHYC4jMYctenIPDC+hIK12NvDMk2ZItboKaDIV1fMHSRlJTYuVD5 # C4lh8zYGNRiER9vcG9H9stQcxWv2XFJRXRLbJbqvUAV6bMURHXLvjflSxIUXk8A8 # FdsaN8cIFRg/eKtFtvUeh17aj54WcmnGrnu3tz5q4i6tAgMBAAGjggHdMIIB2TAS # BgkrBgEEAYI3FQEEBQIDAQABMCMGCSsGAQQBgjcVAgQWBBQqp1L+ZMSavoKRPEY1 # Kc8Q/y8E7jAdBgNVHQ4EFgQUn6cVXQBeYl2D9OXSZacbUzUZ6XIwXAYDVR0gBFUw # UzBRBgwrBgEEAYI3TIN9AQEwQTA/BggrBgEFBQcCARYzaHR0cDovL3d3dy5taWNy # b3NvZnQuY29tL3BraW9wcy9Eb2NzL1JlcG9zaXRvcnkuaHRtMBMGA1UdJQQMMAoG # CCsGAQUFBwMIMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMAsGA1UdDwQEAwIB # hjAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFNX2VsuP6KJcYmjRPZSQW9fO # mhjEMFYGA1UdHwRPME0wS6BJoEeGRWh0dHA6Ly9jcmwubWljcm9zb2Z0LmNvbS9w # a2kvY3JsL3Byb2R1Y3RzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNybDBaBggr # BgEFBQcBAQROMEwwSgYIKwYBBQUHMAKGPmh0dHA6Ly93d3cubWljcm9zb2Z0LmNv # bS9wa2kvY2VydHMvTWljUm9vQ2VyQXV0XzIwMTAtMDYtMjMuY3J0MA0GCSqGSIb3 # DQEBCwUAA4ICAQCdVX38Kq3hLB9nATEkW+Geckv8qW/qXBS2Pk5HZHixBpOXPTEz # tTnXwnE2P9pkbHzQdTltuw8x5MKP+2zRoZQYIu7pZmc6U03dmLq2HnjYNi6cqYJW # AAOwBb6J6Gngugnue99qb74py27YP0h1AdkY3m2CDPVtI1TkeFN1JFe53Z/zjj3G # 82jfZfakVqr3lbYoVSfQJL1AoL8ZthISEV09J+BAljis9/kpicO8F7BUhUKz/Aye # ixmJ5/ALaoHCgRlCGVJ1ijbCHcNhcy4sa3tuPywJeBTpkbKpW99Jo3QMvOyRgNI9 # 5ko+ZjtPu4b6MhrZlvSP9pEB9s7GdP32THJvEKt1MMU0sHrYUP4KWN1APMdUbZ1j # dEgssU5HLcEUBHG/ZPkkvnNtyo4JvbMBV0lUZNlz138eW0QBjloZkWsNn6Qo3GcZ # KCS6OEuabvshVGtqRRFHqfG3rsjoiV5PndLQTHa1V1QJsWkBRH58oWFsc/4Ku+xB # Zj1p/cvBQUl+fpO+y/g75LcVv7TOPqUxUYS8vwLBgqJ7Fx0ViY1w/ue10CgaiQuP # Ntq6TPmb/wrpNPgkNWcr4A245oyZ1uEi6vAnQj0llOZ0dFtq0Z4+7X6gMTN9vMvp # e784cETRkPHIqzqKOghif9lwY1NNje6CbaUFEMFxBmoQtB1VM1izoXBm8qGCA00w # ggI1AgEBMIH5oYHRpIHOMIHLMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGlu # Z3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBv # cmF0aW9uMSUwIwYDVQQLExxNaWNyb3NvZnQgQW1lcmljYSBPcGVyYXRpb25zMScw # JQYDVQQLEx5uU2hpZWxkIFRTUyBFU046MzcwMy0wNUUwLUQ5NDcxJTAjBgNVBAMT # HE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2WiIwoBATAHBgUrDgMCGgMVAInb # HtxB+OlGyQnxQYhy04KSYSSPoIGDMIGApH4wfDELMAkGA1UEBhMCVVMxEzARBgNV # BAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jv # c29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAg # UENBIDIwMTAwDQYJKoZIhvcNAQELBQACBQDqN0Z9MCIYDzIwMjQwNzA5MDUxNzQ5 # WhgPMjAyNDA3MTAwNTE3NDlaMHQwOgYKKwYBBAGEWQoEATEsMCowCgIFAOo3Rn0C # AQAwBwIBAAICNqkwBwIBAAICE64wCgIFAOo4l/0CAQAwNgYKKwYBBAGEWQoEAjEo # MCYwDAYKKwYBBAGEWQoDAqAKMAgCAQACAwehIKEKMAgCAQACAwGGoDANBgkqhkiG # 9w0BAQsFAAOCAQEAWqpU8EU+bSHTnx5r+ldpaGs9aTsJp/3CKIcVvvwXSIGA5s8Z # P+jnM78xsXwWQU/XauVXOjHbYDv+iv8GECMVRpua20VxsCxHY0qWxEViK4lJg5qo # M+yLkO88bWU/90IBSWtwNu1tmQ1Osc05dMqoAgDiqCAbT2rJ2kKVd0dmaHPtyI37 # Nkm0kg+BLjy4XAT92lmweHCHlzEOSFNEV7OHzjQyBECQLylQwy3s9Zi3vfKclbEL # zXA5o85ZhtjpETjjKu1W+LAZ17DStBo/bMiD1UXt4FxdhvlPM+RmsnlCCtuEop29 # yH/CmeuZTTO/SdqUGxhdQCC9F5+dTw9THXSEaTGCBA0wggQJAgEBMIGTMHwxCzAJ # BgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25k # MR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jv # c29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwAhMzAAAB6pokctVZP2FjAAEAAAHqMA0G # CWCGSAFlAwQCAQUAoIIBSjAaBgkqhkiG9w0BCQMxDQYLKoZIhvcNAQkQAQQwLwYJ # KoZIhvcNAQkEMSIEIKS4yNwMesX3Wlqz3rhCW9TKRvsvW7uO93+Nz2mYnxS4MIH6 # BgsqhkiG9w0BCRACLzGB6jCB5zCB5DCBvQQgKY+h1eNkNHiLCDSW0sA1cGHkbW4q # ooi+ryyMp6S4ZngwgZgwgYCkfjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2Fz # aGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENv # cnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAx # MAITMwAAAeqaJHLVWT9hYwABAAAB6jAiBCDjS1pWQBtpggyeIgCMv6946D8fJSfU # b1otM7qnXGulKjANBgkqhkiG9w0BAQsFAASCAgBzkUk15OOUDGZJCjAqrQNqi3cs # W5hGbmRJ5T0HzmcvKP0jZaoD5lXmXofcip4OeHRGsWCNsHq7FHf8vgCyfIqnyUFB # wZ3hjWOlaaXxuyMA+LF+HfKe7nmKedNd70YbxDTWUCs9KwEk8GhjD6t2CFL6XEYB # l1YzsqriE/TVoKwIAsgjZmLo1AeIgJC4X5lQJrp8h21+vRekRDsF6M9Y5trSvhB/ # q7GsjZtp1UUD8DfCNBvYtn6nNGDrwokrqIwD9dkREOz5j5YWf7nGSAT4Ox/c/w70 # 2XY6mZMTLRs8xqG/oaQkVsYnezvptGCYN9xWIYWpTD+ki4yJUKvEZ124p1jZPzaj # 9z95cbfA8bVaNidopFIgivYeszwHT7J5yOzzqlL/NzNOe93ndYcmlytAe+yWraJI # nUiH+4vDlkjgnLh4xp8bcj8ARvTEGGWebSvQuMKaZR4nPVramzizezWz921TccvF # ifMZ9ywWClDEPlIUms/zQvFprIHZUhU4EhcSTOS0oLAg6Avq7OfD11GWvqQVF2I5 # 8PMyX5fux/HrcTShIA6hEJqT1NkZLY0dJQGzieqEDYlpvVJNPGb23KRAcXa6pJvX # /BJempmob7xeryR383qudVTYk2PwZjemRL4p3wM3Dl0ppmS42rbuBewTJsK1q9Gt # 1dIS1FAFOUuSMPnrbA== # SIG # End signature block |