AzStackHciMOCStack/AzStackHci.MOCStack.Helpers.psm1
Import-LocalizedData -BindingVariable VvsTxt -FileName AzStackHci.MOCStack.Strings.psd1 class HealthModel { # Attributes for Azure Monitor schema [string]$Name #Name of the individual test/rule/alert that was executed. Unique, not exposed to the customer. [string]$Title #User-facing name; one or more sentences indicating the direct issue. [string]$Severity #Severity of the result (Critical, Warning, Informational, Hidden) - this answers how important the result is. Critical is the only update-blocking severity. [string]$Description #Detailed overview of the issue and what impact the issue has on the stamp. [psobject]$Tags #Key-value pairs that allow grouping/filtering individual tests. For example, "Group": "ReadinessChecks", "UpdateType": "ClusterAware" [string]$Status #The status of the check running (i.e. Failed, Succeeded, In Progress) - this answers whether the check ran, and passed or failed. [string]$Remediation #Set of steps that can be taken to resolve the issue found. [string]$TargetResourceID #The unique identifier for the affected resource (such as a node or drive). [string]$TargetResourceName #The name of the affected resource. [string]$TargetResourceType #The type of resource being referred to (well-known set of nouns in infrastructure, aligning with Monitoring). [datetime]$Timestamp #The Time in which the HealthCheck was called. [psobject[]]$AdditionalData #Property bag of key value pairs for additional information. [string]$HealthCheckSource #The name of the services called for the HealthCheck (I.E. Test-AzureStack, Test-Cluster). } class AzStackHciMOCStackVolumeTarget : HealthModel { # Attribute for performing check [string]$ExpectedMOCStackVolumeSize [string]$CurrentMOCStackVolumeSize } class AzStackHciMOCStackCPUCoreTarget : HealthModel { # Attribute for performing check [string]$ExpectedMOCStackCPUCoreCount [string]$CurrentMOCStackCPUCoreCount } class AzStackHciMOCStackRAMTarget : HealthModel { # Attribute for performing check [string]$ExpectedMOCStackRAM [string]$CurrentMOCStackRAM } class AzStackHciMOCStackPortTarget : HealthModel { # Attribute for performing check [string]$ExpectedEnablePort [string]$DisablePort } class AzStackHciMOCStackFirewallURLTarget : HealthModel { # Attribute for performing check [string]$ExpectedAllowedURL [string]$BlockedFirewallURL } class AzStackHciMOCStackNodeAgentTarget : HealthModel {} class AzStackHciMOCStackClouldAgentTarget : HealthModel {} class AzStackHciMOCStackClusterNodeTarget : HealthModel {} function Test-MOCStackVolume { <# .SYNOPSIS Verify if the available free space in the volume, meets the size threshold required by MOCStack during the deployment scenario. .DESCRIPTION Verify if the available free space in the volume meets the size threshold required by MOCStack during the deployment scenario. .PARAMETER PsSession Specify the PsSession(s) used to validation from. .PARAMETER OperationType Specify the Operation Type to target for MOCStack validation. e.g. Deployment, Update, etc .PARAMETER PhysicalDriveLetter Specify PhysicalDriveLetter used to validation MOCStack Volume. Default C drive is used as MOCStack volume. #> [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [System.Management.Automation.Runspaces.PSSession[]] $PsSession, [Parameter(Mandatory = $false)] [string[]] $OperationType, [Parameter(Mandatory=$false, ParameterSetName="DefaultSet")] [string] $PhysicalDriveLetter = "C" ) try { $defalutHC4MOCStackVolumeSize = 50 $defalutVMMOCStackVolumeSize = 20 Log-Info -Message ($VvsTxt.MOCStackVolumeStartInfo) -Type Info Log-Info -Message ($VvsTxt.MOCStackVolumeDriveInfo -f $PhysicalDriveLetter) $lowDiskMsg = ($VvsTxt.LowDiskSpaceMsg -f $PhysicalDriveLetter) # Scriptblock to test MOCStackVolumeSize on each server $testVolumeSb = { $AdditionalData = @() $status = "Succeeded" $errorMsg = $null $hardwareType = $null $expectedMOCStackVolumeSizeInGB = $args[0] $freeSpaceInGB = 1 $resourceMsg = $null try { # Check if env is Virtual $hardwareType = (Get-WmiObject -Class Win32_ComputerSystem).Model if ($hardwareType -eq "Virtual Machine") { $expectedMOCStackVolumeSizeInGB = $args[1] } # Check free space on physical volume $totalFreeSpace = (Get-Volume -DriveLetter $args[2]).SizeRemaining $freeSpaceInGB = [int]($totalFreeSpace / 1GB) if ($freeSpaceInGB -lt $expectedMOCStackVolumeSizeInGB) { $resourceMsg = "MOCStack volume '$($args[2])' needs, $($expectedMOCStackVolumeSizeInGB) GB free space." throw $args[3] } } catch { $errorMsg = $_.Exception.Message $resourceMsg = "Error occurred in Environment Validator MOCStack Volume test." $status = "Failed" } finally { $AdditionalData += New-Object -TypeName PsObject -Property @{ HardwareType = $hardwareType ExpectedMOCStackVolumeSize = $expectedMOCStackVolumeSizeInGB CurrentMOCStackVolumeSize = $freeSpaceInGB Status = $status Source = $ENV:COMPUTERNAME Resource = $resourceMsg Detail = $errorMsg } } return $AdditionalData } # Run scriptblock $MOCStackVolumeSizeResult = Invoke-Command -Session $PsSession -ScriptBlock $testVolumeSb -ArgumentList $defalutHC4MOCStackVolumeSize, $defalutVMMOCStackVolumeSize, $PhysicalDriveLetter, $lowDiskMsg # build result $now = [datetime]::UtcNow $targetComputerName = if ($PsSession.PSComputerName) { $PsSession.PSComputerName } else { $ENV:COMPUTERNAME } $aggregateStatus = if ($MOCStackVolumeSizeResult.Status -contains 'Failed') { 'Failed' } else { 'Succeeded' } $volumeResult = New-Object -Type AzStackHciMOCStackVolumeTarget -Property @{ Name = 'AzStackHci_MOCStack_Volume' Title = 'MOCStack Volume Requirement' Severity = 'Critical' Description = 'Test to check MOCStack volume size requirement is met' Tags = $OperationType Remediation = 'Free up disk space for MOCStack Volume' TargetResourceID = $targetComputerName TargetResourceName = $targetComputerName TargetResourceType = $MOCStackVolumeSizeResult.HardwareType | Get-Unique Timestamp = $now Status = $aggregateStatus AdditionalData = $MOCStackVolumeSizeResult HealthCheckSource = $ENV:EnvChkrId ExpectedMOCStackVolumeSize = $MOCStackVolumeSizeResult.ExpectedMOCStackVolumeSize CurrentMOCStackVolumeSize = $MOCStackVolumeSizeResult.CurrentMOCStackVolumeSize } return $volumeResult } catch { throw $_ } } function Test-MOCStackCPUCore { <# .SYNOPSIS Verify if the host node meets the minimum CPU count requirement for MOCStack configuration .DESCRIPTION Verify if the host node meets the minimum CPU count requirement for MOCStack configuration .PARAMETER PsSession Specify the PsSession(s) used to validation from. .PARAMETER OperationType Specify the Operation Type to target for MOCStack validation. e.g. Deployment, Update, etc #> [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [System.Management.Automation.Runspaces.PSSession[]] $PsSession, [Parameter(Mandatory = $false)] [string[]] $OperationType ) try { $defalutCPUCount = 4 Log-Info -Message ($VvsTxt.MOCStackCPUStartInfo) -Type Info $lowCpuMsg = ($lvsTxt.LowCPuMsg) # Scriptblock to test MOCStackCpu core on each server $testCpuSb = { $AdditionalData = @() $status = "Succeeded" $errorMsg = $null $hardwareType = $null $expectedMOCStackCpuCoreCount = $args[0] $resourceMsg = $null $cpuCount = 1 try { # Check CPU core count on each machince $cpuCount = $(Get-CimInstance -ClassName Win32_Processor | Select-Object -Property NumberOfCores).NumberOfCores if ($cpuCount -lt $expectedMOCStackCpuCoreCount) { $resourceMsg = "MOCStack CPU validation expects at least the host to have $($expectedMOCStackCpuCoreCount) cores." throw $args[1] } } catch { $errorMsg = $_.Exception.Message $resourceMsg = "Error occurred in Environment Validator MOCStack Cpu test." $status = "Failed" } finally { $AdditionalData += New-Object -TypeName PsObject -Property @{ HardwareType = $hardwareType ExpectedMOCStackCPUCoreCount = $expectedMOCStackCpuCoreCount CurrentMOCStackCPUCoreCount = $cpuCount Status = $status Source = $ENV:COMPUTERNAME Resource = $resourceMsg Detail = $errorMsg } } return $AdditionalData } # Run scriptblock $MOCStackCPUResult = Invoke-Command -Session $PsSession -ScriptBlock $testCpuSb -ArgumentList $defalutCPUCount, $lowCpuMsg # build result $now = [datetime]::UtcNow $targetComputerName = if ($PsSession.PSComputerName) { $PsSession.PSComputerName } else { $ENV:COMPUTERNAME } $aggregateStatus = if ($MOCStackCPUResult.Status -contains 'Failed') { 'Failed' } else { 'Succeeded' } $cpuCoreResult = New-Object -Type AzStackHciMOCStackCPUCoreTarget -Property @{ Name = 'AzStackHci_MOCStack_CpuCoreCount' Title = 'MOCStack CPU Requirement' Severity = 'Critical' Description = 'Test to check MOCStack CPU requirement is met' Tags = $OperationType Remediation = 'Upgrage the node CPU core configuration' TargetResourceID = $targetComputerName TargetResourceName = $targetComputerName TargetResourceType = $MOCStackCPUResult.HardwareType | Get-Unique Timestamp = $now Status = $aggregateStatus AdditionalData = $MOCStackCPUResult HealthCheckSource = $ENV:EnvChkrId ExpectedMOCStackCPUCoreCount = $MOCStackCPUResult.ExpectedMOCStackCPUCoreCount CurrentMOCStackCPUCoreCount = $MOCStackCPUResult.CurrentMOCStackCPUCoreCount } return $cpuCoreResult } catch { throw $_ } } function Test-MOCStackMemory { <# .SYNOPSIS Verify physical memory ie RAM of the node satisfies the minimum requirements of MOCStack. .DESCRIPTION Verify physical memory ie RAM of the node satisfies the minimum requirements of MOCStack. .PARAMETER PsSession Specify the PsSession(s) used to validation from. .PARAMETER OperationType Specify the Operation Type to target for MOCStack validation. e.g. Deployment, Update, etc #> [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [System.Management.Automation.Runspaces.PSSession[]] $PsSession, [Parameter(Mandatory = $false)] [string[]] $OperationType ) try { $defalutRAMSizeInGB = 8 Log-Info -Message ($VvsTxt.MOCStackMemoryStartInfo) -Type Info $lowMemoryMsg = ($VvsTxt.LowMemoryMsg) # Scriptblock to test physical memory on each server $testRAMSb = { $AdditionalData = @() $status = "Succeeded" $errorMsg = $null $hardwareType = $null $expectedRAMSizeInGB = $args[0] $freeSpaceInGB = 1 $resourceMsg = $null try { # Check physical memory size $totalRAM = (Get-CimInstance Win32_PhysicalMemory | Measure-Object -Property capacity -Sum).sum $totalRAMInGB = [int]($totalRAM / 1GB) if ($totalRAMInGB -lt $defalutRAMSize) { $resourceMsg = "MOCStack physical memory size validation expects at least the host to have $($expectedRAMSizeInGB) GB." throw $args[1] } } catch { $errorMsg = $_.Exception.Message $resourceMsg = "Error occurred in Environment Validator MOCStack physical memory size test." $status = "Failed" } finally { $AdditionalData += New-Object -TypeName PsObject -Property @{ HardwareType = $hardwareType ExpectedMOCStackRAM = $expectedRAMSizeInGB CurrentMOCStackRAM = $totalRAMInGB Status = $status Source = $ENV:COMPUTERNAME Resource = $resourceMsg Detail = $errorMsg } } return $AdditionalData } # Run scriptblock $MOCStackRAMSizeResult = Invoke-Command -Session $PsSession -ScriptBlock $testRAMSb -ArgumentList $defalutRAMSizeInGB, $lowMemoryMsg # build result $now = [datetime]::UtcNow $targetComputerName = if ($PsSession.PSComputerName) { $PsSession.PSComputerName } else { $ENV:COMPUTERNAME } $aggregateStatus = if ($MOCStackRAMSizeResult.Status -contains 'Failed') { 'Failed' } else { 'Succeeded' } $RAMResult = New-Object -Type AzStackHciMOCStackRAMTarget -Property @{ Name = 'AzStackHci_MOCStack_RAM_Size' Title = 'MOCStack RAM Requirement' Severity = 'Critical' Description = 'Test to check MOCStack RAM requirement is met' Tags = $OperationType Remediation = 'Upgrage the node PhysicalMemory configuration' TargetResourceID = $targetComputerName TargetResourceName = $targetComputerName TargetResourceType = $MOCStackRAMSizeResult.HardwareType | Get-Unique Timestamp = $now Status = $aggregateStatus AdditionalData = $MOCStackRAMSizeResult HealthCheckSource = $ENV:EnvChkrId ExpectedMOCStackRAM = $MOCStackRAMSizeResult.ExpectedMOCStackRAM CurrentMOCStackRAM = $MOCStackRAMSizeResult.CurrentMOCStackRAM } return $RAMResult } catch { throw $_ } } function Test-MOCStackNetworkPort { <# .SYNOPSIS Verify that the required network ports for MOCStack are open. .DESCRIPTION Verify that the required network ports for MOCStack are open. .PARAMETER PsSession Specify the PsSession(s) used to validation from. .PARAMETER OperationType Specify the Operation Type to target for MOCStack validation. e.g. Deployment, Update, etc #> [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [System.Management.Automation.Runspaces.PSSession[]] $PsSession, [Parameter(Mandatory = $false)] [string[]] $OperationType ) try { $portList = '443','80' Log-Info -Message ($VvsTxt.MOCStackPortInfo) -Type Info $disabledPortMsg = ($VvsTxt.DisablePortMsg) # Scriptblock to test network port on each server $testPortSb = { $AdditionalData = @() $status = "Succeeded" $errorMsg = $null $hardwareType = $null $expectedPortList = $args[0] $failedPort = $null $resourceMsg = $null try { # Check each network port is enabled on the node foreach ($port in $expectedPortList) { $tcpSucceeded = Test-NetConnection -Port $port -InformationLevel Quiet if($tcpSucceeded -ne $true) { $failedPort += " $port," $status = "Failed" } } # Check overall network port enable status if ($status -eq 'Failed') { $resourceMsg = "The network port validation for MOCStack requires $($failedPort) to be enabled." throw $args[1] } } catch { $errorMsg = $_.Exception.Message $resourceMsg = "Error occurred in Environment Validator MOCStack network port test." $status = "Failed" } finally { $AdditionalData += New-Object -TypeName PsObject -Property @{ HardwareType = $hardwareType ExpectedEnablePort = $portList DisablePort = $failedPort Status = $status Source = $ENV:COMPUTERNAME Resource = $resourceMsg Detail = $errorMsg } } return $AdditionalData } # Run scriptblock $MOCStackPortResult = Invoke-Command -Session $PsSession -ScriptBlock $testPortSb -ArgumentList $portList, $disabledPortMsg # build result $now = [datetime]::UtcNow $targetComputerName = if ($PsSession.PSComputerName) { $PsSession.PSComputerName } else { $ENV:COMPUTERNAME } $aggregateStatus = if ($MOCStackPortResult.Status -contains 'Failed') { 'Failed' } else { 'Succeeded' } $PortResult = New-Object -Type AzStackHciMOCStackPortTarget -Property @{ Name = 'AzStackHci_MOCStack_Network_Port' Title = 'MOCStack Network Port Requirement' Severity = 'Critical' Description = 'Test to check MOCStack Network Port requirement is met' Tags = $OperationType Remediation = 'Enable the mandatory network port required for MOCStack' TargetResourceID = $targetComputerName TargetResourceName = $targetComputerName TargetResourceType = $MOCStackPortResult.HardwareType | Get-Unique Timestamp = $now Status = $aggregateStatus AdditionalData = $MOCStackPortResult HealthCheckSource = $ENV:EnvChkrId ExpectedEnablePort = $MOCStackPortResult.ExpectedEnablePort DisablePort = $MOCStackPortResult.DisablePort } return $PortResult } catch { throw $_ } } function Test-MOCStackFirewallUrl { <# .SYNOPSIS Verify that the necessary URL for MOCStack is added to the allowlist in the firewall. .DESCRIPTION Verify that the necessary URL for MOCStack is added to the allowlist in the firewall. .PARAMETER PsSession Specify the PsSession(s) used to validation from. .PARAMETER OperationType Specify the Operation Type to target for MOCStack validation. e.g. Deployment, Update, etc #> [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [System.Management.Automation.Runspaces.PSSession[]] $PsSession, [Parameter(Mandatory = $false)] [string[]] $OperationType ) try { Log-Info -Message ($VvsTxt.MOCStackFirewallURLInfo) -Type Info $blockedURLMsg = ($VvsTxt.BlockedURLMsg) $fireWallURLList = @{ 'mcr.microsoft.com'= 443 'azure.com'= 443 'windows.net'= 443 'sts.windows.net'= 443 'hybridaks.azurecr.io'= 443 'guestnotificationservice.azure.com'= 443 'ecpacr.azurecr.io'= 443 'microsoft.com'= 443 'azurearcfork8s.azurecr.io'= 443 'pythonhosted.org'= 443 'msk8s.sf.tlu.dl.delivery.mp.microsoft.com'= 443 'msk8s.b.tlu.dl.delivery.mp.microsoft.com'= 80 'msk8s.api.cdp.microsoft.com'= 443 'msk8s.sb.tlu.dl.delivery.mp.microsoft.com'= 443 'kvamanagementoperator.azurecr.io'= 443 'linuxgeneva-microsoft.azurecr.io'= 443 } # Scriptblock to test firewall URLs in allowlist on each server $testFirewllURLSb = { $AdditionalData = @() $status = "Succeeded" $errorMsg = $null $hardwareType = $null $expectedfireWallURLList = $args[0] $blockedURLList = $null $resourceMsg = $null try { # Check each URLs is in allowlist on the node foreach ($url in $expectedfireWallURLList.keys) { $retryCount = 0 $connectionSucceeded = $false while (!$connectionSucceeded -and $retryCount -lt 5) { $checkConnection = Test-NetConnection -ComputerName $url -Port $expectedfireWallURLList[$url] $connectionSucceeded = $checkConnection.TcpTestSucceeded $retryCount ++ } if($connectionSucceeded -eq $false) { $blockedURLList += " $url," $status = "Failed" } } # Check overall allowlist URLs status if ($status -eq 'Failed') { $resourceMsg = "The MOCStack requires $($blockedURLList) URL to be in firewall allowed list." throw $args[1] } } catch { $errorMsg = $_.Exception.Message $resourceMsg = "Error occurred in Environment Validator MOCStack Firewall Url test." $status = "Failed" } finally { $AdditionalData += New-Object -TypeName PsObject -Property @{ HardwareType = $hardwareType ExpectedAllowedURL = $expectedfireWallURLList BlockedFirewallURL = $blockedURLList Status = $status Source = $ENV:COMPUTERNAME Resource = $resourceMsg Detail = $errorMsg } } return $AdditionalData } # Run scriptblock $MOCStackURLResult = Invoke-Command -Session $PsSession -ScriptBlock $testFirewllURLSb -ArgumentList $fireWallURLList, $blockedURLMsg # build result $now = [datetime]::UtcNow $targetComputerName = if ($PsSession.PSComputerName) { $PsSession.PSComputerName } else { $ENV:COMPUTERNAME } $aggregateStatus = if ($MOCStackURLResult.Status -contains 'Failed') { 'Failed' } else { 'Succeeded' } $UrlResult = New-Object -Type AzStackHciMOCStackFirewallURLTarget -Property @{ Name = 'AzStackHci_MOCStack_Firewall_URL' Title = 'MOCStack Firewall URL allowed list Requirement' Severity = 'Critical' Description = 'Test to check MOCStack Firewall URL allowed list requirement is met' Tags = $OperationType Remediation = 'Enable the mandatory MOCStack URL' TargetResourceID = $targetComputerName TargetResourceName = $targetComputerName TargetResourceType = $MOCStackURLResult.HardwareType | Get-Unique Timestamp = $now Status = $aggregateStatus AdditionalData = $MOCStackURLResult HealthCheckSource = $ENV:EnvChkrId ExpectedAllowedURL = $MOCStackURLResult.ExpectedAllowedURL BlockedFirewallURL = $MOCStackURLResult.BlockedFirewallURL } return $UrlResult } catch { throw $_ } } function Test-MOCStackNodeAgents { <# .SYNOPSIS Verify MOC NodeAgent Service is up and running. .DESCRIPTION Verify MOC NodeAgent Service is up and running. .PARAMETER PsSession Specify the PsSession(s) used to validation from. .PARAMETER OperationType Specify the Operation Type to target for MOCStack validation. e.g. Deployment, Update, etc #> [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [System.Management.Automation.Runspaces.PSSession[]] $PsSession, [Parameter(Mandatory = $false)] [string[]] $OperationType ) try { Log-Info -Message ($VvsTxt.MOCStackNodeAgentInfo) -Type Info $nodeAgentFailMsg = ($VvsTxt.NodeAgentFail) # Scriptblock to check MOC Node agent service on each server $testNodeAgentSb = { $AdditionalData = @() $status = "Succeeded" $errorMsg = $null $hardwareType = $null $expectedAgentState = 'Running' $currentAgentStatus = $null $resourceMsg = $null try { # Check Node agent service is in running state $currentAgentStatus = $(Get-Service -Name 'wssdagent' | Select Status).Status if($currentAgentStatus -ne $expectedAgentState) { $status = "Failed" $resourceMsg = "On node $($ENV:COMPUTERNAME), MOC NodeAgent service is in $($currentAgentStatus) status" throw $args[0] } } catch { $errorMsg = $_.Exception.Message $resourceMsg = "Error occurred in Environment Validator MOCStack Node Agent test." $status = "Failed" } finally { $AdditionalData += New-Object -TypeName PsObject -Property @{ HardwareType = $hardwareType Status = $status Source = $ENV:COMPUTERNAME Resource = $resourceMsg Detail = $errorMsg } } return $AdditionalData } # Run scriptblock $MOCStackNodeAgentResult = Invoke-Command -Session $PsSession -ScriptBlock $testNodeAgentSb -ArgumentList $nodeAgentFailMsg # build result $now = [datetime]::UtcNow $targetComputerName = if ($PsSession.PSComputerName) { $PsSession.PSComputerName } else { $ENV:COMPUTERNAME } $aggregateStatus = if ($MOCStackNodeAgentResult.Status -contains 'Failed') { 'Failed' } else { 'Succeeded' } $NodeAgentResult = New-Object -Type AzStackHciMOCStackNodeAgentTarget -Property @{ Name = 'AzStackHci_MOCStack_NodeAgent_Service' Title = 'MOCStack Node agent Service State' Severity = 'Critical' Description = 'Test to check if the MOCStack NodeAgent service is in the expected running state.' Tags = $OperationType Remediation = 'Ensure the MOC NodeAgent service is up and running.' TargetResourceID = $targetComputerName TargetResourceName = $targetComputerName TargetResourceType = $MOCStackNodeAgentResult.HardwareType | Get-Unique Timestamp = $now Status = $aggregateStatus AdditionalData = $MOCStackNodeAgentResult HealthCheckSource = $ENV:EnvChkrId } return $NodeAgentResult } catch { throw $_ } } function Test-MOCStackCloudAgent { <# .SYNOPSIS Verify MOC CloudAgent Service is in an online state. .DESCRIPTION Verify MOC CloudAgent Service is in an online state. .PARAMETER PsSession Specify the PsSession(s) used to validation from. .PARAMETER OperationType Specify the Operation Type to target for MOCStack validation. e.g. Deployment, Update, etc #> [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [System.Management.Automation.Runspaces.PSSession[]] $PsSession, [Parameter(Mandatory = $false)] [string[]] $OperationType ) try { Log-Info -Message ($VvsTxt.MOCStackCloudAgentInfo) -Type Info $cloudAgentFailMsg = ($VvsTxt.CloudAgentFail) # Scriptblock to check MOC cloud agent service state $testCloudAgentSb = { $AdditionalData = @() $status = "Succeeded" $errorMsg = $null $hardwareType = $null $expectedCloudAgentState = 'Online' $currentCloudAgentStatus = $null $resourceMsg = $null try { # Check Cloud agent service is in Online state $currentCloudAgentStatus = $(Get-ClusterResource -Name 'MOC Cloud Agent Service' | select State).State if($currentCloudAgentStatus -ne $expectedCloudAgentState) { $status = "Failed" $resourceMsg = "MOC CloudAgent service is in $($currentCloudAgentStatus) state" throw $args[0] } } catch { $errorMsg = $_.Exception.Message $resourceMsg = "Error occurred in Environment Validator MOCStack CloudAgent Agent test." $status = "Failed" } finally { $AdditionalData += New-Object -TypeName PsObject -Property @{ HardwareType = $hardwareType Status = $status Source = $ENV:COMPUTERNAME Resource = $resourceMsg Detail = $errorMsg } } return $AdditionalData } # Run scriptblock $MOCStackCloudAgentResult = Invoke-Command -Session $PsSession -ScriptBlock $testCloudAgentSb -ArgumentList $cloudAgentFailMsg # build result $now = [datetime]::UtcNow $targetComputerName = if ($PsSession.PSComputerName) { $PsSession.PSComputerName } else { $ENV:COMPUTERNAME } $aggregateStatus = if ($MOCStackCloudAgentResult.Status -contains 'Failed') { 'Failed' } else { 'Succeeded' } $CloudAgentResult = New-Object -Type AzStackHciMOCStackClouldAgentTarget -Property @{ Name = 'AzStackHci_MOCStack_CloudAgent_Service' Title = 'MOCStack CloudAgent Service State' Severity = 'Critical' Description = 'Test to check if the MOCStack CloudAgent service is in the expected Online state.' Tags = $OperationType Remediation = 'Ensure the MOC CloudAgent service is in Online state.' TargetResourceID = $targetComputerName TargetResourceName = $targetComputerName TargetResourceType = $MOCStackCloudAgentResult.HardwareType | Get-Unique Timestamp = $now Status = $aggregateStatus AdditionalData = $MOCStackCloudAgentResult HealthCheckSource = $ENV:EnvChkrId } return $CloudAgentResult } catch { throw $_ } } function Test-MOCStackClusterNode { <# .SYNOPSIS Verify cluster node is up and running. .DESCRIPTION Verify cluster node is up and running. .PARAMETER PsSession Specify the PsSession(s) used to validation from. .PARAMETER OperationType Specify the Operation Type to target for MOCStack validation. e.g. Deployment, Update, etc #> [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [System.Management.Automation.Runspaces.PSSession[]] $PsSession, [Parameter(Mandatory = $false)] [string[]] $OperationType ) try { Log-Info -Message ($VvsTxt.ClusterNodeInfo) -Type Info $clusterNodeFailMsg = ($VvsTxt.ClusterNodeFail) # Scriptblock to check cluster node state $testClusterNodeSb = { $AdditionalData = @() $status = "Succeeded" $errorMsg = $null $hardwareType = $null $expectedClusterNodeState = 'Up' $offlineNode = $null $resourceMsg = $null try { # Check cluster node state is up and running $offlineNode = $(Get-ClusterNode | Where State -ne $expectedClusterNodeState) if($offlineNode -ne $null -and $offlineNode.count -gt 0) { $status = "Failed" $resourceMsg = "Cluster node $($offlineNode.Name), in $($offlineNode.State) state" throw $args[0] } } catch { $errorMsg = $_.Exception.Message $resourceMsg = "Error occurred in Environment Validator MOCStack Cluster Node test." $status = "Failed" } finally { $AdditionalData += New-Object -TypeName PsObject -Property @{ HardwareType = $hardwareType Status = $status Source = $ENV:COMPUTERNAME Resource = $resourceMsg Detail = $errorMsg } } return $AdditionalData } # Run scriptblock $MOCStackClusterNodeResult = Invoke-Command -Session $PsSession -ScriptBlock $testClusterNodeSb -ArgumentList $cloudAgentFailMsg # build result $now = [datetime]::UtcNow $targetComputerName = if ($PsSession.PSComputerName) { $PsSession.PSComputerName } else { $ENV:COMPUTERNAME } $aggregateStatus = if ($MOCStackClusterNodeResult.Status -contains 'Failed') { 'Failed' } else { 'Succeeded' } $ClusterNodeResult = New-Object -Type AzStackHciMOCStackClusterNodeTarget -Property @{ Name = 'AzStackHci_MOCStack_ClusterNode_State' Title = 'MOCStack Cluster Node State' Severity = 'Critical' Description = 'Test to check if the cluster node is in the expected up state.' Tags = $OperationType Remediation = 'Ensure the Cluster Node is in up and running state.' TargetResourceID = $targetComputerName TargetResourceName = $targetComputerName TargetResourceType = $MOCStackClusterNodeResult.HardwareType | Get-Unique Timestamp = $now Status = $aggregateStatus AdditionalData = $MOCStackClusterNodeResult HealthCheckSource = $ENV:EnvChkrId } return $ClusterNodeResult } catch { throw $_ } } Export-ModuleMember -Function Test-MOCStackCPUCore Export-ModuleMember -Function Test-MOCStackMemory Export-ModuleMember -Function Test-MOCStackNetworkPort Export-ModuleMember -Function Test-MOCStackFirewallUrl Export-ModuleMember -Function Test-MOCStackClusterNode Export-ModuleMember -Function Test-MOCStackCloudAgent Export-ModuleMember -Function Test-MOCStackNodeAgents # Excluding Volume check function #Export-ModuleMember -Function Test-MOCStackVolume # SIG # Begin signature block # MIInvwYJKoZIhvcNAQcCoIInsDCCJ6wCAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCDlmeuBuL+8coXc # YzlQkj1Zt6KlyDBuJPBBLKkoh+6GpqCCDXYwggX0MIID3KADAgECAhMzAAADTrU8 # esGEb+srAAAAAANOMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD # VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p # bmcgUENBIDIwMTEwHhcNMjMwMzE2MTg0MzI5WhcNMjQwMzE0MTg0MzI5WjB0MQsw # CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u # ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB # AQDdCKiNI6IBFWuvJUmf6WdOJqZmIwYs5G7AJD5UbcL6tsC+EBPDbr36pFGo1bsU # p53nRyFYnncoMg8FK0d8jLlw0lgexDDr7gicf2zOBFWqfv/nSLwzJFNP5W03DF/1 # 1oZ12rSFqGlm+O46cRjTDFBpMRCZZGddZlRBjivby0eI1VgTD1TvAdfBYQe82fhm # WQkYR/lWmAK+vW/1+bO7jHaxXTNCxLIBW07F8PBjUcwFxxyfbe2mHB4h1L4U0Ofa # +HX/aREQ7SqYZz59sXM2ySOfvYyIjnqSO80NGBaz5DvzIG88J0+BNhOu2jl6Dfcq # jYQs1H/PMSQIK6E7lXDXSpXzAgMBAAGjggFzMIIBbzAfBgNVHSUEGDAWBgorBgEE # AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUnMc7Zn/ukKBsBiWkwdNfsN5pdwAw # RQYDVR0RBD4wPKQ6MDgxHjAcBgNVBAsTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEW # MBQGA1UEBRMNMjMwMDEyKzUwMDUxNjAfBgNVHSMEGDAWgBRIbmTlUAXTgqoXNzci # tW2oynUClTBUBgNVHR8ETTBLMEmgR6BFhkNodHRwOi8vd3d3Lm1pY3Jvc29mdC5j # b20vcGtpb3BzL2NybC9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDctMDguY3JsMGEG # CCsGAQUFBwEBBFUwUzBRBggrBgEFBQcwAoZFaHR0cDovL3d3dy5taWNyb3NvZnQu # Y29tL3BraW9wcy9jZXJ0cy9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDctMDguY3J0 # MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggIBAD21v9pHoLdBSNlFAjmk # mx4XxOZAPsVxxXbDyQv1+kGDe9XpgBnT1lXnx7JDpFMKBwAyIwdInmvhK9pGBa31 # TyeL3p7R2s0L8SABPPRJHAEk4NHpBXxHjm4TKjezAbSqqbgsy10Y7KApy+9UrKa2 # kGmsuASsk95PVm5vem7OmTs42vm0BJUU+JPQLg8Y/sdj3TtSfLYYZAaJwTAIgi7d # hzn5hatLo7Dhz+4T+MrFd+6LUa2U3zr97QwzDthx+RP9/RZnur4inzSQsG5DCVIM # pA1l2NWEA3KAca0tI2l6hQNYsaKL1kefdfHCrPxEry8onJjyGGv9YKoLv6AOO7Oh # JEmbQlz/xksYG2N/JSOJ+QqYpGTEuYFYVWain7He6jgb41JbpOGKDdE/b+V2q/gX # UgFe2gdwTpCDsvh8SMRoq1/BNXcr7iTAU38Vgr83iVtPYmFhZOVM0ULp/kKTVoir # IpP2KCxT4OekOctt8grYnhJ16QMjmMv5o53hjNFXOxigkQWYzUO+6w50g0FAeFa8 # 5ugCCB6lXEk21FFB1FdIHpjSQf+LP/W2OV/HfhC3uTPgKbRtXo83TZYEudooyZ/A # Vu08sibZ3MkGOJORLERNwKm2G7oqdOv4Qj8Z0JrGgMzj46NFKAxkLSpE5oHQYP1H # tPx1lPfD7iNSbJsP6LiUHXH1MIIHejCCBWKgAwIBAgIKYQ6Q0gAAAAAAAzANBgkq # 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 # /Xmfwb1tbWrJUnMTDXpQzTGCGZ8wghmbAgEBMIGVMH4xCzAJBgNVBAYTAlVTMRMw # EQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVN # aWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNp # Z25pbmcgUENBIDIwMTECEzMAAANOtTx6wYRv6ysAAAAAA04wDQYJYIZIAWUDBAIB # BQCgga4wGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEO # MAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIPikz+TzboEWw8b9sjhwO6iQ # oI/CjC3j5sQ8i5AOd0e/MEIGCisGAQQBgjcCAQwxNDAyoBSAEgBNAGkAYwByAG8A # cwBvAGYAdKEagBhodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20wDQYJKoZIhvcNAQEB # BQAEggEAOfJzMcV9CAeUv82gWkZIvDcdpU5OfzNHtvgk+30ZOFRm/HAnK15ZDvSg # hFaktZkjbr+83PYnt1MQ9VfScdArwkWlJl9LxMrHgmdiX8iQ8fMnNNaAu6/4vNzf # ITcKu0fRsAvIH0PzhHHjsiNZGIz03Y96UKM0DsWQmdy7vJale1YzGfYXoeLBPkyV # LNbjespPscta0Opr75dRnZ/MNjruwyGtSns+2m4senX/2DIV2/KyGUx3II12k/NQ # mrw4tj3lnXwaXn2zN6c+bWF/cJyoMHInToG5ChPu1qL2xiBlzt2pnRf+eBI57gFw # XRsBukc+VA8+jwDOJxPgel+iUYJagqGCFykwghclBgorBgEEAYI3AwMBMYIXFTCC # FxEGCSqGSIb3DQEHAqCCFwIwghb+AgEDMQ8wDQYJYIZIAWUDBAIBBQAwggFZBgsq # hkiG9w0BCRABBKCCAUgEggFEMIIBQAIBAQYKKwYBBAGEWQoDATAxMA0GCWCGSAFl # AwQCAQUABCBKUJD2ZEdSj6urPC7DvOWkDCjrbPfIbl4O1wWF70ic6AIGZJL88dL3 # GBMyMDIzMDcxMzE5MjQ1Mi43MDdaMASAAgH0oIHYpIHVMIHSMQswCQYDVQQGEwJV # UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UE # ChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMS0wKwYDVQQLEyRNaWNyb3NvZnQgSXJl # bGFuZCBPcGVyYXRpb25zIExpbWl0ZWQxJjAkBgNVBAsTHVRoYWxlcyBUU1MgRVNO # OjhENDEtNEJGNy1CM0I3MSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBT # ZXJ2aWNloIIReDCCBycwggUPoAMCAQICEzMAAAGz/iXOKRsbihwAAQAAAbMwDQYJ # KoZIhvcNAQELBQAwfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24x # EDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlv # bjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTAwHhcNMjIw # OTIwMjAyMjAzWhcNMjMxMjE0MjAyMjAzWjCB0jELMAkGA1UEBhMCVVMxEzARBgNV # BAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jv # c29mdCBDb3Jwb3JhdGlvbjEtMCsGA1UECxMkTWljcm9zb2Z0IElyZWxhbmQgT3Bl # cmF0aW9ucyBMaW1pdGVkMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjo4RDQxLTRC # RjctQjNCNzElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2VydmljZTCC # AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALR8D7rmGICuLLBggrK9je3h # JSpc9CTwbra/4Kb2eu5DZR6oCgFtCbigMuMcY31QlHr/3kuWhHJ05n4+t377PHon # dDDbz/dU+q/NfXSKr1pwU2OLylY0sw531VZ1sWAdyD2EQCEzTdLD4KJbC6wmACon # iJBAqvhDyXxJ0Nuvlk74rdVEvribsDZxzClWEa4v62ENj/HyiCUX3MZGnY/AhDya # zfpchDWoP6cJgNCSXmHV9XsJgXJ4l+AYAgaqAvN8N+EpN+0TErCgFOfwZV21cg7v # genOV48gmG/EMf0LvRAeirxPUu+jNB3JSFbW1WU8Z5xsLEoNle35icdET+G3wDNm # cSXlQYs4t94IWR541+PsUTkq0kmdP4/1O4GD54ZsJ5eUnLaawXOxxT1fgbWb9VRg # 1Z4aspWpuL5gFwHa8UNMRxsKffor6qrXVVQ1OdJOS1JlevhpZlssSCVDodMc30I3 # fWezny6tNOofpfaPrtwJ0ukXcLD1yT+89u4uQB/rqUK6J7HpkNu0fR5M5xGtOch9 # nyncO9alorxDfiEdb6zeqtCfcbo46u+/rfsslcGSuJFzlwENnU+vQ+JJ6jJRUrB+ # mr51zWUMiWTLDVmhLd66//Da/YBjA0Bi0hcYuO/WctfWk/3x87ALbtqHAbk6i1cJ # 8a2coieuj+9BASSjuXkBAgMBAAGjggFJMIIBRTAdBgNVHQ4EFgQU0BpdwlFnUgwY # izhIIf9eBdyfw40wHwYDVR0jBBgwFoAUn6cVXQBeYl2D9OXSZacbUzUZ6XIwXwYD # VR0fBFgwVjBUoFKgUIZOaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9j # cmwvTWljcm9zb2Z0JTIwVGltZS1TdGFtcCUyMFBDQSUyMDIwMTAoMSkuY3JsMGwG # CCsGAQUFBwEBBGAwXjBcBggrBgEFBQcwAoZQaHR0cDovL3d3dy5taWNyb3NvZnQu # Y29tL3BraW9wcy9jZXJ0cy9NaWNyb3NvZnQlMjBUaW1lLVN0YW1wJTIwUENBJTIw # MjAxMCgxKS5jcnQwDAYDVR0TAQH/BAIwADAWBgNVHSUBAf8EDDAKBggrBgEFBQcD # CDAOBgNVHQ8BAf8EBAMCB4AwDQYJKoZIhvcNAQELBQADggIBAFqGuzfOsAm4wAJf # ERmJgWW0tNLLPk6VYj53+hBmUICsqGgj9oXNNatgCq+jHt03EiTzVhxteKWOLoTM # x39cCcUJgDOQIH+GjuyjYVVdOCa9Fx6lI690/OBZFlz2DDuLpUBuo//v3e4Kns41 # 2mO3A6mDQkndxeJSsdBSbkKqccB7TC/muFOhzg39mfijGICc1kZziJE/6HdKCF8p # 9+vs1yGUR5uzkIo+68q/n5kNt33hdaQ234VEh0wPSE+dCgpKRqfxgYsBT/5tXa3e # 8TXyJlVoG9jwXBrKnSQb4+k19jHVB3wVUflnuANJRI9azWwqYFKDbZWkfQ8tpNoF # fKKFRHbWomcodP1bVn7kKWUCTA8YG2RlTBtvrs3CqY3mADTJUig4ckN/MG6AIr8Q # +ACmKBEm4OFpOcZMX0cxasopdgxM9aSdBusaJfZ3Itl3vC5C3RE97uURsVB2pvC+ # CnjFtt/PkY71l9UTHzUCO++M4hSGSzkfu+yBhXMGeBZqLXl9cffgYPcnRFjQT97G # b/bg4ssLIFuNJNNAJub+IvxhomRrtWuB4SN935oMfvG5cEeZ7eyYpBZ4DbkvN44Z # vER0EHRakL2xb1rrsj7c8I+auEqYztUpDnuq6BxpBIUAlF3UDJ0SMG5xqW/9hLMW # naJCvIerEWTFm64jthAi0BDMwnCwMIIHcTCCBVmgAwIBAgITMwAAABXF52ueAptJ # mQAAAAAAFTANBgkqhkiG9w0BAQsFADCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgT # Cldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29m # dCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJvb3QgQ2VydGlmaWNh # dGUgQXV0aG9yaXR5IDIwMTAwHhcNMjEwOTMwMTgyMjI1WhcNMzAwOTMwMTgzMjI1 # WjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH # UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQD # Ex1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDCCAiIwDQYJKoZIhvcNAQEB # BQADggIPADCCAgoCggIBAOThpkzntHIhC3miy9ckeb0O1YLT/e6cBwfSqWxOdcjK # NVf2AX9sSuDivbk+F2Az/1xPx2b3lVNxWuJ+Slr+uDZnhUYjDLWNE893MsAQGOhg # fWpSg0S3po5GawcU88V29YZQ3MFEyHFcUTE3oAo4bo3t1w/YJlN8OWECesSq/XJp # rx2rrPY2vjUmZNqYO7oaezOtgFt+jBAcnVL+tuhiJdxqD89d9P6OU8/W7IVWTe/d # vI2k45GPsjksUZzpcGkNyjYtcI4xyDUoveO0hyTD4MmPfrVUj9z6BVWYbWg7mka9 # 7aSueik3rMvrg0XnRm7KMtXAhjBcTyziYrLNueKNiOSWrAFKu75xqRdbZ2De+JKR # Hh09/SDPc31BmkZ1zcRfNN0Sidb9pSB9fvzZnkXftnIv231fgLrbqn427DZM9itu # qBJR6L8FA6PRc6ZNN3SUHDSCD/AQ8rdHGO2n6Jl8P0zbr17C89XYcz1DTsEzOUyO # ArxCaC4Q6oRRRuLRvWoYWmEBc8pnol7XKHYC4jMYctenIPDC+hIK12NvDMk2ZItb # oKaDIV1fMHSRlJTYuVD5C4lh8zYGNRiER9vcG9H9stQcxWv2XFJRXRLbJbqvUAV6 # bMURHXLvjflSxIUXk8A8FdsaN8cIFRg/eKtFtvUeh17aj54WcmnGrnu3tz5q4i6t # AgMBAAGjggHdMIIB2TASBgkrBgEEAYI3FQEEBQIDAQABMCMGCSsGAQQBgjcVAgQW # BBQqp1L+ZMSavoKRPEY1Kc8Q/y8E7jAdBgNVHQ4EFgQUn6cVXQBeYl2D9OXSZacb # UzUZ6XIwXAYDVR0gBFUwUzBRBgwrBgEEAYI3TIN9AQEwQTA/BggrBgEFBQcCARYz # aHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9Eb2NzL1JlcG9zaXRvcnku # aHRtMBMGA1UdJQQMMAoGCCsGAQUFBwMIMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIA # QwBBMAsGA1UdDwQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFNX2 # VsuP6KJcYmjRPZSQW9fOmhjEMFYGA1UdHwRPME0wS6BJoEeGRWh0dHA6Ly9jcmwu # bWljcm9zb2Z0LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY1Jvb0NlckF1dF8yMDEw # LTA2LTIzLmNybDBaBggrBgEFBQcBAQROMEwwSgYIKwYBBQUHMAKGPmh0dHA6Ly93 # d3cubWljcm9zb2Z0LmNvbS9wa2kvY2VydHMvTWljUm9vQ2VyQXV0XzIwMTAtMDYt # MjMuY3J0MA0GCSqGSIb3DQEBCwUAA4ICAQCdVX38Kq3hLB9nATEkW+Geckv8qW/q # XBS2Pk5HZHixBpOXPTEztTnXwnE2P9pkbHzQdTltuw8x5MKP+2zRoZQYIu7pZmc6 # U03dmLq2HnjYNi6cqYJWAAOwBb6J6Gngugnue99qb74py27YP0h1AdkY3m2CDPVt # I1TkeFN1JFe53Z/zjj3G82jfZfakVqr3lbYoVSfQJL1AoL8ZthISEV09J+BAljis # 9/kpicO8F7BUhUKz/AyeixmJ5/ALaoHCgRlCGVJ1ijbCHcNhcy4sa3tuPywJeBTp # kbKpW99Jo3QMvOyRgNI95ko+ZjtPu4b6MhrZlvSP9pEB9s7GdP32THJvEKt1MMU0 # sHrYUP4KWN1APMdUbZ1jdEgssU5HLcEUBHG/ZPkkvnNtyo4JvbMBV0lUZNlz138e # W0QBjloZkWsNn6Qo3GcZKCS6OEuabvshVGtqRRFHqfG3rsjoiV5PndLQTHa1V1QJ # sWkBRH58oWFsc/4Ku+xBZj1p/cvBQUl+fpO+y/g75LcVv7TOPqUxUYS8vwLBgqJ7 # Fx0ViY1w/ue10CgaiQuPNtq6TPmb/wrpNPgkNWcr4A245oyZ1uEi6vAnQj0llOZ0 # dFtq0Z4+7X6gMTN9vMvpe784cETRkPHIqzqKOghif9lwY1NNje6CbaUFEMFxBmoQ # tB1VM1izoXBm8qGCAtQwggI9AgEBMIIBAKGB2KSB1TCB0jELMAkGA1UEBhMCVVMx # EzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoT # FU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEtMCsGA1UECxMkTWljcm9zb2Z0IElyZWxh # bmQgT3BlcmF0aW9ucyBMaW1pdGVkMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjo4 # RDQxLTRCRjctQjNCNzElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2Vy # dmljZaIjCgEBMAcGBSsOAwIaAxUAcYtE6JbdHhKlwkJeKoCV1JIkDmGggYMwgYCk # fjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH # UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQD # Ex1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDANBgkqhkiG9w0BAQUFAAIF # AOhaexYwIhgPMjAyMzA3MTMyMTMxMDJaGA8yMDIzMDcxNDIxMzEwMlowdDA6Bgor # BgEEAYRZCgQBMSwwKjAKAgUA6Fp7FgIBADAHAgEAAgIFIzAHAgEAAgIT1TAKAgUA # 6FvMlgIBADA2BgorBgEEAYRZCgQCMSgwJjAMBgorBgEEAYRZCgMCoAowCAIBAAID # B6EgoQowCAIBAAIDAYagMA0GCSqGSIb3DQEBBQUAA4GBAA6WnuPAJmXUSj5Yp21/ # 2le/lG+jm2M7tD5lqqAznYlbV4GEu0HQZ26AiC06jZnBkdYxOBsLeQu7vNfTt0Q7 # M8m/74Q5yiJ6KN20D2NFM/Hgygl6MMxqXcQz9Tj96ScYEEhwpw1xlrQMteRMjoND # wGKabA3C2M7WRyYimX8sVpyqMYIEDTCCBAkCAQEwgZMwfDELMAkGA1UEBhMCVVMx # EzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoT # FU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUt # U3RhbXAgUENBIDIwMTACEzMAAAGz/iXOKRsbihwAAQAAAbMwDQYJYIZIAWUDBAIB # BQCgggFKMBoGCSqGSIb3DQEJAzENBgsqhkiG9w0BCRABBDAvBgkqhkiG9w0BCQQx # IgQg98iNDSBjI8lKsdo7ARH2x3aelku56r0uuHVwwLQBGbYwgfoGCyqGSIb3DQEJ # EAIvMYHqMIHnMIHkMIG9BCCGoTPVKhDSB7ZG0zJQZUM2jk/ll1zJGh6KOhn76k+/ # QjCBmDCBgKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAw # DgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24x # JjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwAhMzAAABs/4l # zikbG4ocAAEAAAGzMCIEIELDJ4uTAIvOOC4GCl91uiSlXIFlmiKLgYrkmWHKBejV # MA0GCSqGSIb3DQEBCwUABIICAAs+L6OYKJBC3FAi3fq+lptSczDPyyzCBP2XeAR/ # W60cyc8ogbOa7etB9TElauXSSNGzIuv2BpG/bW707ucGbsuOSzrhkCJTiB1daket # rNpcMfaE29PrON74rO8culvNZb7oitsUUPlNE9mUMxu6Da/vwLIiGZUW3BS4lS6I # qeB89WJ6J38iRBYLNONEmvl++PRmFtnZAaXlpt4GIOq4IM3g+WmeyjVGKFLAmRc0 # NEL2Whj5HLi18S0V4NGqGowmcZWcbfL8qlTnOmslPqRNLidWug+Xz6a7uuWy1QIW # Q8CcW90Q6O8mrql/5YekLWFrii5VcmwKbAjBUSwj+NEy+zq25AWYLQtJ4yrY4XES # DpPanKFT1k7kDsyoVrURk+VDZCNq7LtqawIv2r5LECJFxzVs6aJAWqcJ6TNYvNn/ # Bh5F4c6TPoMC4NNY5T+M/x/x/rRRXp7lXeA/dg9ETCwb7DYMShBeRDTa46/nEbEG # wdjEAX6XmblWAkVSqqdIeSobZHFoArUl4POTuOdTFQoPRZdUIcvbfXMUTu4hl27W # K+lL1AUlfEM5VHNZklnuycKu9qK2JvEsnoP1yKGA8SD5W7CDYSzwaZAife32R0EG # mXkPgOKvQCgV+Ip22AqHRKRnMPMj18rnVNiYdklUWdLvmQvFYvbXgkPlkuSWIWiu # K2Jm # SIG # End signature block |