InstallMarvellQLogicFCPowerKit.psm1
|
#/************************************************************************ #* * #* NOTICE * #* * #* COPYRIGHT 2019-2026 Marvell Semiconductor, Inc * #* ALL RIGHTS RESERVED * #* * #* This computer program is CONFIDENTIAL and contains TRADE SECRETS of * #* Marvell Semiconductor, Inc. The receipt or possession of this * #* program does not convey any rights to reproduce or disclose its * #* contents, or to manufacture, use, or sell anything that it may * #* describe, in whole or in part, without the specific written consent * #* of Marvell Semiconductor, Inc. * #* Any reproduction of this program without the express written consent * #* of Marvell Semiconductor, Inc is a violation of the copyright laws * #* and may subject you to civil liability and criminal prosecution. * #* * #************************************************************************/ # Helper Functions Function Out-Log { # This function controls console output, displaying it only if the Verbose variable is set. # This function now has another parameter, Level. Commands that may output messages now have a level parameter set. Key is below. # Level = 0 - Completely silent except for success / failure messages. # Level = 1 - Default if $Script:verbose is not set, about 5 output messages per added appx. # Level = 2 - Level 1 + Appcmd output messages, Add-AppxPackage messages # Level = 3 - Level 2 + Registry, XML, enabling of features, WinRM, setx, IO, netsh, etc. Very verbose. Param ( [Parameter(Mandatory=$true)] [AllowNull()] $Data, [Parameter(Mandatory=$true)] [AllowNull()] $Level, [Parameter(Mandatory=$false)] [AllowNull()] $ForegroundColor, [Parameter(Mandatory=$false)] [AllowNull()] [Switch] $NoNewline ) if ($Script:Verbose -eq $null) { $Script:Verbose = 1 } if ($Data -eq $null) { return } $Data = ($Data | Out-String) $Data = $Data.Substring(0, $Data.Length - 2) # Remove extra `n from Out-String (only one) $Data = $Data.Replace('"', '`"') $expression = "Write-Host -Object `"$Data`"" if ($ForegroundColor -ne $null) { $expression += " -ForegroundColor $ForegroundColor" } if ($NoNewline) { $expression += " -NoNewline" } if ($Level -le $Script:verbose) { Invoke-Expression $expression } } Function Test-ExecutionPolicy { # This function checks to make sure the ExecutionPolicy is not Unrestricted and if it is, prompts the user to change it to either RemoteSigned or Bypass. # If the ExecutionPolicy is left at Unrestricted, the user will receive one prompt for every cmdlet each time they open a new PowerShell window. $currentExecutionPolicy = Get-ExecutionPolicy if ($currentExecutionPolicy -eq 'Unrestricted') { $title = 'Execution Policy Configuration' $message = "Your current execution policy is $currentExecutionPolicy. This policy causes PowerShell to prompt you every session for every cmdlet. Set the execution policy to RemoteSigned or Bypass to avoid this." $remoteSigned = New-Object System.Management.Automation.Host.ChoiceDescription '&RemoteSigned', ` 'Sets the execution policy to RemoteSigned.' $bypass = New-Object System.Management.Automation.Host.ChoiceDescription '&Bypass', ` 'Sets the execution policy to Bypass.' $ignoreAndContinue = New-Object System.Management.Automation.Host.ChoiceDescription '&Ignore and Continue', ` 'Does not change the execution policy.' $options = [System.Management.Automation.Host.ChoiceDescription[]]($remoteSigned, $bypass, $ignoreAndContinue) $tempResult = $host.ui.PromptForChoice($title, $message, $options, 0) if ($tempResult -eq 0) { $newExecutionPolicy = 'RemoteSigned' } if ($tempResult -eq 1) { $newExecutionPolicy = 'Bypass' } if ($tempResult -eq 2) { return } # If the user has set a CurrentUser sceope level ExecutionPolicy, then change this to RemoteSigned/Bypass as well. if ((Get-ExecutionPolicy -Scope CurrentUser) -ne 'Undefined') { Set-ExecutionPolicy -ExecutionPolicy $newExecutionPolicy -Scope CurrentUser -Force } Set-ExecutionPolicy -ExecutionPolicy $newExecutionPolicy -Scope LocalMachine -Force Out-Log -Data "ExecutionPolicy changed from $currentExecutionPolicy to $(Get-ExecutionPolicy)." -Level 0 } } Function Test-IfNano { # Check if OS is Nano or Non-Nano $envInfo = [Environment]::OSVersion.Version $envInfoCimInst = Get-CimInstance Win32_OperatingSystem return ( ($envInfo.Major -eq 10) -and ($envInfo.Minor -eq 0) -and ($envInfoCimInst.ProductType -eq 3) -and (($envInfoCimInst.OperatingSystemSKU -eq 143) -or ($envInfoCimInst.OperatingSystemSKU -eq 144) ) ) } Function Test-IfServerCore { # Check if OS is Server Core $regKey = 'hklm:/software/microsoft/windows nt/currentversion' return (Get-ItemProperty $regKey).InstallationType -eq 'Server Core' } Function Get-CmdletsAppxFileName { if ($Script:isNano) { return '.\MRVLFCProvider-WindowsServerNano.appx' } else { return '.\MRVLFCProvider-WindowsServer.appx' } } Function Add-ManyToRegistryWriteOver { # This function changes multiple registry values at once. # Path is the path to the registry key to change, e.g. "HKLM:\Software\Classes\CLSID\{A31B8A5E-8B6A-421C-8BB1-F85C9C34E659}\InprocServer32" # NameTypeValueArray is an array of arrays of size 3 containing Name, Type, and Value for the registry entry, e.g. # @( # @('(Default)', 'String', "C:\Program Files\WindowsApps\MRVLFCProvider_1.0.1.0_x64_NorthAmerica_yym61f5wjvd3m\MRVLFCProvider.dll"), # @('ThreadingModel', 'String', 'Free') # ) Param ( [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] $Path, [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] $NameTypeValueArray ) # if path doesn't exist, create it if (-not (Test-Path -LiteralPath $Path)) { Out-Log -Data (New-Item -Path $Path -Force 2>&1) -Level 3 } $arr = $NameTypeValueArray if ($arr[0] -isnot [System.Array]) { Out-Log -Data (New-ItemProperty -LiteralPath $Path -Name $arr[0] -Type $arr[1] -Value $arr[2] -Force 2>&1) -Level 3 return } foreach ($ntv in $arr) { Out-Log -Data (New-ItemProperty -LiteralPath $Path -Name $ntv[0] -Type $ntv[1] -Value $ntv[2] -Force 2>&1) -Level 3 } } Function Test-RegistryValue { Param ( [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] $Path, [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] $Value ) try { Out-Log -Data (Get-ItemProperty -Path $Path | Select-Object -ExpandProperty $Value -ErrorAction Stop 2>&1) -Level 3 return $true } catch { return $false } } Function ConvertFrom-SecureToPlain { Param( [Parameter(Mandatory=$true)] [System.Security.SecureString] $SecurePassword ) $passwordPointer = [Runtime.InteropServices.Marshal]::SecureStringToBSTR($SecurePassword) $plainTextPassword = [Runtime.InteropServices.Marshal]::PtrToStringAuto($passwordPointer) [Runtime.InteropServices.Marshal]::ZeroFreeBSTR($passwordPointer) return $plainTextPassword } Function Convert-CredentialToHeaderAuthorizationString { Param ( [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] $Credential ) return [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes("$($Credential.UserName)`:$(ConvertFrom-SecureToPlain -SecurePassword $Credential.Password)")) } Function Get-AppxPackageWrapper { # This function tries the Get-AppxPackage command several times before really failing. # This is necessary to prevent some intermittent issues with Get-AppxPackage. # This may no longer be necessary as it seems to only happen after an Add-AppxProvisionedPackage which is no longer used. Param ( [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] $AppxName ) $numAttempts = 3 $numSecondsBetweenAttempts = 5 for ($i=0; $i -lt $numAttempts; $i++) { $appxPkg = Get-AppxPackage | Where-Object { $_.name -eq $AppxName } if ($appxPkg -ne $null) { return $appxPkg } Out-Log -Data ("Couldn't find Appx package. Trying again in $numSecondsBetweenAttempts seconds (attempt $($i+1) of $numAttempts) ...") -ForegroundColor DarkRed -Level 1 Start-Sleep -Seconds $numSecondsBetweenAttempts } Out-Log -Data 'Failed to find Appx package. Please try running this script again.' -ForegroundColor Red -Level 0 Exit } Function Invoke-IOWrapper { # This function tries the Expression several times before really failing. # This is necessary to prevent some intermittent issues with certain IO commands, especially Copy-Item. Param ( [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] $Expression, [Parameter(Mandatory=$false)] [ValidateNotNullOrEmpty()] $SuccessMessage, [Parameter(Mandatory=$false)] [ValidateNotNullOrEmpty()] $FailMessage ) $numAttempts = 5 $numSecondsBetweenAttempts = 1 $Expression += ' -ErrorAction Stop' for ($i=0; $i -lt $numAttempts; $i++) { try { Invoke-Expression $Expression if ($i -ne 0) { Out-Log -Data 'Success!' -ForegroundColor DarkGreen -Level 3 } Out-Log -Data $SuccessMessage -Level 1 return } catch { if ($i -ne 0) { Out-Log -Data 'Failed.' -ForegroundColor DarkRed -Level 3 } Out-Log -Data "$FailMessage Trying again in $numSecondsBetweenAttempts seconds (attempt $($i+1) of $numAttempts) ... " -ForegroundColor DarkRed -NoNewline -Level 3 Start-Sleep -Seconds $numSecondsBetweenAttempts $Global:err = $_ } } Out-Log -Data 'Failed.' -ForegroundColor DarkRed -Level 3 Out-Log -Data "$FailMessage Failed after $numAttempts attempts with $numSecondsBetweenAttempts second(s) between each attempt." -ForegroundColor Red -Level 0 Out-Log -Data "Failed to install MarvellQLogicFCPowerKit. Before installing MarvellQLogicFCPowerKit, uninstall the same, reboot the server and try installing again." -ForegroundColor Red -Level 0 Exit } # Prompts Function Set-WinRMConfigurationPrompt { if ($Script:isNano) { return $false } # Skip if Nano if ($ConfigureWinRM) { return $true } $title = 'WinRM Configuration' $message = 'Do you want to configure WinRM in order to connect to Linux machines remotely?' $yes = New-Object System.Management.Automation.Host.ChoiceDescription '&Yes', ` 'Configures WinRM to allow remote connections to Linux machines.' $no = New-Object System.Management.Automation.Host.ChoiceDescription '&No', ` 'Does not configure WinRM. Remote connection to Linux machines will not be possible.' $options = [System.Management.Automation.Host.ChoiceDescription[]]($yes, $no) $tempResult = $host.ui.PromptForChoice($title, $message, $options, 0) if ($tempResult -eq 0) { return $true } else { return $false } } Function Get-CredentialWrapper { Param ( [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] $AddAccountToIISResponse ) if (-not $AddAccountToIISResponse) { return $null } try { return Get-Credential -Message 'Enter the credentials for the account you would like to be added to IIS' -UserName $env:USERNAME } catch { Out-Log -Data 'No credentials supplied; no account will be added to IIS.' -Level 1 } } Function Install-PoshSSHModule { if ($Script:isNano) { return $false } # Skip if Nano if ($InstallPoshSSHModule) { return $true } $title = 'Install-Posh-SSH_Module' $message = 'Do you want to install Posh-SSH_Module in-order to manage [Linux and VMware_ESXi Remote Host Systems] or not ?' $yes = New-Object System.Management.Automation.Host.ChoiceDescription '&Yes', 'install Posh-SSH_Module.' $no = New-Object System.Management.Automation.Host.ChoiceDescription '&No', 'Does not install Posh-SSH_Module.' $options = [System.Management.Automation.Host.ChoiceDescription[]]($yes, $no) $tempResult = $host.ui.PromptForChoice($title, $message, $options, 0) if ($tempResult -eq 0) { return $true } else { return $false } } Function Install-RESTServerPrompt { if ($Script:isNano) { return $false } # Skip if Nano if ($InstallRESTServer) { return $true } $title = 'Install-REST Server' $message = 'Do you want to install the REST server to be able to invoke FC PowerShell cmdlets via REST?' $yes = New-Object System.Management.Automation.Host.ChoiceDescription '&Yes', 'Installs the REST server.' $no = New-Object System.Management.Automation.Host.ChoiceDescription '&No', 'Does not install the REST server.' $options = [System.Management.Automation.Host.ChoiceDescription[]]($yes, $no) $tempResult = $host.ui.PromptForChoice($title, $message, $options, 0) if ($tempResult -eq 0) { return $true } else { return $false } } Function Get-RESTServerProtocolPrompt { $defaultValue = 'http' while ($true) { $response = Read-Host -Prompt "Would you like to use http or https? [$defaultValue] ?" if ([string]::IsNullOrWhiteSpace($response)) { return $defaultValue } elseif ($response -ne 'http' -and $response -ne 'https' -and $response -ne 'both') { Out-Log -Data "Response must be either: 'http' or 'https'; please try again.`n" -ForegroundColor Red -Level 0 } else { return $response } } } Function Get-RESTServerPortPrompt { Param ( [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] $Protocol ) $defaultValue = '7777' if ($Protocol -eq 'https') { $defaultValue = '7778' } while ($true) { $response = Read-Host -Prompt "What $($Protocol.ToUpper()) port would you like to use for the REST server [$defaultValue] ?" if ([string]::IsNullOrWhiteSpace($response)) { return $defaultValue } $responseAsInt = $response -as [int] if ($responseAsInt -eq $null -or $responseAsInt -lt 1 -or $responseAsInt -gt 65535) { Out-Log -Data "Response must be a valid port number; please try again.`n" -ForegroundColor Red -Level 0 } else { return $response } } return $response } Function Get-RESTServerExistingHTTPSCert { $title = 'REST Server HTTPS Certificate Preference' $message = "Would you like to create a self-signed HTTPS certificate?`nSelecting no will allow you to choose the location of your existing HTTPS certificate." $yes = New-Object System.Management.Automation.Host.ChoiceDescription '&Yes', 'Creates a self-signed HTTPS certificate.' $no = New-Object System.Management.Automation.Host.ChoiceDescription '&No', 'Opens file selection dialog for location of existing HTTPS certificate.' $options = [System.Management.Automation.Host.ChoiceDescription[]]($yes, $no) while ($true) { $tempResult = $host.ui.PromptForChoice($title, $message, $options, 0) if ($tempResult -eq 0) { return $null # create self-signed cert } $fileBrowser = New-Object System.Windows.Forms.OpenFileDialog -Property @{ Title = 'Select HTTPS certificate to use for the REST server' InitialDirectory = (Get-Location).Path Filter = 'Certificate File (*.pfx)|*.pfx' Multiselect = $false } $null = $fileBrowser.ShowDialog() if (-not [string]::IsNullOrWhiteSpace($fileBrowser.FileName)) { return $fileBrowser.FileName } } } Function Get-RESTServerHTTPSCertPasswordPrompt { while ($true) { $password1 = Read-Host -AsSecureString 'Please enter a password for the HTTPS certificate' $password2 = Read-Host -AsSecureString 'Please re-enter the password for the HTTPS certificate' $password1Text = ConvertFrom-SecureToPlain -SecurePassword $password1 $password2Text = ConvertFrom-SecureToPlain -SecurePassword $password2 if ([string]::IsNullOrWhiteSpace($password1Text) -or [string]::IsNullOrWhiteSpace($password2Text)) { Out-Log -Data "One or both passwords are empty; please try again.`n" -ForegroundColor Red -Level 0 } elseif ($password1Text -ne $password2Text) { Out-Log -Data "Passwords do not match; please try again.`n" -ForegroundColor Red -Level 0 } else { return $password1 } } } Function Get-RESTServerHTTPSCertPasswordPromptSimple { $password = Read-Host -AsSecureString 'Please enter the password for the HTTPS certificate' return $password } Function Test-HTTPSCertPassword { Param ( [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] $CertificateFilePath, [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] $SecurePassword ) $passwordText = ConvertFrom-SecureToPlain -SecurePassword $SecurePassword $null = certutil.exe -p $passwordText -dump $CertificateFilePath 2>&1 return $LASTEXITCODE -eq 0 } Function Test-LastExitCode { Param ( [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] $ExitCode, [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] $Message, [Parameter(Mandatory=$false)] [AllowNull()] [Switch] $ExitScriptIfBadErrorCode ) if ($ExitCode -eq 0) { return; } if (-not $ExitScriptIfBadErrorCode) { Out-Log -Data "WARNING: $Message" -ForegroundColor Yellow -Level 1 } else { Out-Log -Data "ERROR: $Message" -ForegroundColor Red -Level 1 Exit } } # Main Functions Function Add-AppxTrustedAppsRegistryKey { # This function remembers user's current AllowAllTrustedApps registry setting and then changes it to # allow installation of all trusted apps. if ($Script:isNano) { return } # Skip if Nano if ($Script:isServerCore) { return } # Skip if Server Core # Remember user's registry setting before modify it to be able to set it back later. $appxPolicyRegKey = 'HKLM:\Software\Policies\Microsoft\Windows\Appx' $appxPolicyRegValName = 'AllowAllTrustedApps' if (Test-Path -Path $appxPolicyRegKey) { $appxPolicyRegValData = (Get-ItemProperty -Path $appxPolicyRegKey).$appxPolicyRegValName if ($appxPolicyRegValData -ne $null) { $Script:savedAppxPolicyRegValData = $appxPolicyRegValData } Start-Sleep -Milliseconds 1000 # Otherwise access denied error when do Add-ManyToRegistryWriteOver below } Add-ManyToRegistryWriteOver -Path $appxPolicyRegKey -NameTypeValueArray @( @($appxPolicyRegValName, 'DWord', 1) ) } Function Remove-CLSIDRegistryPaths { # if (-not $Script:isNano) { return } # Skip if NOT Nano if (Test-Path -Path "Registry::HKCR\CLSID\$Script:CLSID") { Remove-Item -Path "Registry::HKCR\CLSID\$Script:CLSID" -Recurse } if (Test-Path -Path "HKLM:\Software\Classes\CLSID\$Script:CLSID") { Remove-Item -Path "HKLM:\Software\Classes\CLSID\$Script:CLSID" -Recurse } } Function Install-ForServerCore { Param ( [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] $AppxFileName ) $serverCoreInstallPath = "$env:ProgramFiles\Marvell_Semiconductor_Inc\FC_PowerKit" $appxFileNameSimple = $AppxFileName.Replace('.\', '') $appxZipFileName = $appxFileNameSimple.Replace('.appx', '.zip') # Create installation directory if doesn't exist and copy Appx file to it (renamed to .zip) if (-not (Test-Path $serverCoreInstallPath)) { New-Item -Path $serverCoreInstallPath -Type Directory Copy-Item -Path $AppxFileName -Destination "$serverCoreInstallPath\$appxZipFileName" } # Unzip appx file to PowerKit directory Expand-Archive -Path "$serverCoreInstallPath\$appxZipFileName" -DestinationPath $serverCoreInstallPath # Register CIMProvider Register-CimProvider.exe -Namespace Root/qlgcfc -ProviderName MRVLFCProvider -Path "$serverCoreInstallPath\MRVLFCProvider.dll" -verbose -ForceUpdate -HostingModel LocalSystemHost } Function Add-AppxPackageWrapper { Param ( [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] $AppxFileName ) $savedProgressPreference = $Global:ProgressPreference $Global:ProgressPreference = 'SilentlyContinue' Out-Log -Data (Add-AppxPackage $AppxFileName 2>&1) -Level 2 $Global:ProgressPreference = $savedProgressPreference Out-Log -Data "Added '$($AppxFileName.Replace('.\', ''))' AppxPackage." -Level 1 } Function Add-CLSIDRegistryPathsAndMofCompInstall { # if ($Script:isNano) { return } # Skip if Nano # Perform steps of Register-CimProvider.exe -Namespace Root/qlgcfc -ProviderName MRVLFCProvider -Path MRVLFCProvider.dll -verbose -ForceUpdate -HostingModel LocalSystemHost $appxPath = (Get-AppxPackageWrapper -AppxName 'MRVLFCProvider').InstallLocation $mofcompOutput = & mofcomp.exe -N:root\qlgcfc $appxPath\MRVLFCProvider.mof 2>&1 if ($mofcompOutput -match 'Error') { Out-Log -Data "Failed to register '$appxPath\MRVLFCProvider.mof': $($mofcompOutput -match 'Error')" -ForegroundColor Red -Level 1 } Add-ManyToRegistryWriteOver -Path "HKLM:\Software\Classes\CLSID\$Script:CLSID" -NameTypeValueArray @( @('(Default)', 'String', 'MRVLFCProvider') ) Add-ManyToRegistryWriteOver -Path "HKLM:\Software\Classes\CLSID\$Script:CLSID\InprocServer32" -NameTypeValueArray @( @('(Default)', 'String', "$appxPath\MRVLFCProvider.dll"), @('ThreadingModel', 'String', 'Free') ) } Function Remove-AppxTrustedAppsRegistryKey { if ($Script:isNano) { return } # Skip if Nano if ($Script:isServerCore) { return } # Skip if Server Core $appxPolicyRegKey = 'HKLM:\Software\Policies\Microsoft\Windows\Appx' $appxPolicyRegValName = 'AllowAllTrustedApps' # If user previously had this registry key, set it back to what it was before. Otherwise, just delete the registry key. if ($Script:savedAppxPolicyRegValData -ne $null) { Add-ManyToRegistryWriteOver -Path $appxPolicyRegKey -NameTypeValueArray @( @($appxPolicyRegValName, 'DWord', $Script:savedAppxPolicyRegValData) ) } else { Remove-ItemProperty -Path $appxPolicyRegKey -Name $appxPolicyRegValName } } Function Copy-CmdletsToProgramData { # Check for ProgramData directories with old versions and remove them $oldPaths = (Get-ChildItem $env:ProgramData | Where-Object { ($_.Name).StartsWith('MRVLFCProvider_') }).FullName foreach ($oldPath in $oldPaths) { Remove-Item $oldPath -Recurse -Force 2>&1 | Out-Null } # Copy cmdlets to $appxPkg = Get-AppxPackageWrapper -AppxName 'MRVLFCProvider' $cmdletsPath = $appxPkg.InstallLocation + '\Cmdlets\' if (Test-Path $cmdletsPath) { $cmdletsDestPath = "C:\ProgramData\MarvellQLogicFCCmdlets" if (-not (Test-Path $cmdletsDestPath)) { Out-Log -Data (New-Item -ItemType Directory -Path $cmdletsDestPath -Force 2>&1) -Level 3 } Invoke-IOWrapper -Expression "Copy-Item -Path `"$cmdletsPath\*`" -Destination $cmdletsDestPath -Recurse -Force" ` -FailMessage "Failed to copy cmdlets." Import-Module -force $cmdletsDestPath\MarvellQLogicFCCmdlets.psd1 } } Function Copy-CmdletsToWindowsPowerShellModules { # Copy Cmdlets to "C:\Program Files\WindowsPowerShell\Modules" $appxPkg = Get-AppxPackageWrapper -AppxName 'MRVLFCProvider' $cmdletsPath = $appxPkg.InstallLocation + '\Cmdlets\' if (Test-Path $cmdletsPath) { $cmdletsDestPath = "C:\Program Files\WindowsPowerShell\Modules\MarvellQLogicFCCmdlets" if(-not (Test-Path $cmdletsDestPath)) { Out-Log -Data (New-Item -ItemType Directory -Path $cmdletsDestPath -Force 2>&1) -Level 3 } Write-Host $cmdletsPath Write-Host $cmdletsDestPath Invoke-IOWrapper -Expression "Copy-Item -Path `"$cmdletsPath\*`" -Destination `"$cmdletsDestPath\`" -Recurse -Force" ` -FailMessage "Failed to copy cmdlets." } } Function Copy-XMLTemplatesToProgramData { # Copy cmdlets to ProgramData $appxPkg = Get-AppxPackageWrapper -AppxName 'MRVLFCProvider' $xmlsTemplatesPath = $appxPkg.InstallLocation + '\Cmdlets\XML' if (Test-Path $xmlsTemplatesPath) { $xmlsTemplatesDestPath = "C:\Program Files\Marvell_Semiconductor_Inc\FC_PowerKit\XML" if (Test-Path $xmlsTemplatesDestPath){ Remove-Item $xmlsTemplatesDestPath -Recurse -Force } Invoke-IOWrapper -Expression "Copy-Item -Path `"$xmlsTemplatesPath`" -Destination `"$xmlsTemplatesDestPath`" -Recurse -Force" ` -SuccessMessage "XML Templates copied to '$xmlsTemplatesDestPath'." ` -FailMessage "Failed to copy XML Templates." } } Function Copy-DependentLibsAndFilesToWbemPath { $appxPkg = Get-AppxPackageWrapper -AppxName 'MRVLFCProvider' $dependentLibPath = $appxPkg.InstallLocation if (Test-Path $dependentLibPath) { $dependentLibDestPath = "C:\Windows\System32\wbem" # Copy ql2xhai2.dll to "C:\Windows\System32\wbem" Invoke-IOWrapper -Expression "Copy-Item -Path `"$dependentLibPath\ql2xhai2.dll`" -Destination `"$dependentLibDestPath`" -Recurse -Force" ` -SuccessMessage "Copied dependent library to Wbem path : '$dependentLibDestPath'." ` -FailMessage "Failed to copy dependent library : '$dependentLibDestPath'. Please uninstall the old version if present. Please stop 'WMI Provider Host' if running." # Copy adapters_adapter_provider.properties to "C:\Windows\System32\wbem" Invoke-IOWrapper -Expression "Copy-Item -Path `"$dependentLibPath\adapters_adapter_provider.properties`" -Destination `"$dependentLibDestPath`" -Recurse -Force" ` -SuccessMessage "Copied dependent file - adapters_adapter_provider.properties to Wbem path : '$dependentLibDestPath'." ` -FailMessage "Failed to copy dependent file - adapters_adapter_provider.properties : '$dependentLibDestPath'. Please uninstall the old version if present. Please stop 'WMI Provider Host' if running." # Copy nvramdefs to "C:\Windows\System32\wbem" Invoke-IOWrapper -Expression "Copy-Item -Path `"$dependentLibPath\nvramdefs`" -Destination `"$dependentLibDestPath\nvramdefs`" -Recurse -Force" ` -SuccessMessage "Copied dependent files - nvramdefs to Wbem path : '$dependentLibDestPath'." ` -FailMessage "Failed to copy dependent files - nvramdefs : '$dependentLibDestPath'. Please uninstall the old version if present. Please stop 'WMI Provider Host' if running." } } Function Add-ChangedCmdletsPathToPSModulePath { # This function adds cmdlet module path to system PSModulePath environment variable permanently. #$cmdletPath = "C:\Program Files\WindowsPowerShell\Modules\MarvellQLogicFCCmdlets" if ($Script:isServerCore) { $appxCmdletsPath = "$env:ProgramFiles\Marvell_Semiconductor_Inc\FC_PowerKit\Cmdlets" } else { $appxCmdletsPath = (Get-AppxPackageWrapper -AppxName 'MRVLFCProvider').InstallLocation + '\Cmdlets' } if (Test-RegistryValue -Path 'HKLM:\System\CurrentControlSet\Control\Session Manager\Environment' -Value 'PSModulePath') { $currPSModulePathArr = ((Get-ItemProperty -Path 'HKLM:\System\CurrentControlSet\Control\Session Manager\Environment').PSModulePath).Split(';') $newPSModulePathArr = @() # Remove any old paths with MRVLFCProvider from PSModulePath foreach ($path in $currPSModulePathArr) { if (-not $path.Contains('MRVLFCProvider')) { $newPSModulePathArr += $path } } # Add new cmdlet path to PSModulePath $newPSModulePathArr += $cmdletPath # Skip for uninstall script $newPSModulePathStr = $newPSModulePathArr -join ';' # Write PSModulePath to registry and set local session variable Out-Log -Data (& setx /s $env:COMPUTERNAME PSModulePath $newPSModulePathStr /m 2>&1) -Level 3 $env:PSModulePath = $newPSModulePathStr # Out-Log -Data "Added '$cmdletPath' to PSModulePath." -Level 1 } else { # No PSModulePath registry key/value exists, so don't worry about modifying it. Just create a new one. Out-Log -Data (& setx /s $env:COMPUTERNAME PSModulePath $cmdletPath /m 2>&1) -Level 3 $env:PSModulePath = $cmdletPath # Out-Log -Data "Added '$cmdletPath' to PSModulePath." -Level 1 } } Function Add-CmdletsPathToPSModulePath { # This function adds cmdlet module path to system PSModulePath environment variable permanently. if ($Script:isServerCore) { $appxCmdletsPath = "$env:ProgramFiles\Marvell_Semiconductor_Inc\FC_PowerKit\Cmdlets" } else { #$appxCmdletsPath = (Get-AppxPackageWrapper -AppxName 'MRVLFCProvider').InstallLocation + '\Cmdlets' #$appxPkg = Get-AppxPackageWrapper -AppxName 'MRVLFCProvider' #$appxCmdletsPath = "$env:ProgramData\$($appxPkg.PackageFullName)" $appxCmdletsPath = "C:\ProgramData\MarvellQLogicFCCmdlets" } if (Test-RegistryValue -Path 'HKLM:\System\CurrentControlSet\Control\Session Manager\Environment' -Value 'PSModulePath') { $currPSModulePathArr = ((Get-ItemProperty -Path 'HKLM:\System\CurrentControlSet\Control\Session Manager\Environment').PSModulePath).Split(';') $newPSModulePathArr = @() # Remove any old paths with MRVLFCProvider from PSModulePath foreach ($path in $currPSModulePathArr) { if (-not $path.Contains('MRVLFCProvider')) { $newPSModulePathArr += $path } } # Add new cmdlet path to PSModulePath $newPSModulePathArr += $appxCmdletsPath # Skip for uninstall script $newPSModulePathStr = $newPSModulePathArr -join ';' # Write PSModulePath to registry and set local session variable Out-Log -Data (& setx /s $env:COMPUTERNAME PSModulePath $newPSModulePathStr /m 2>&1) -Level 3 $env:PSModulePath = $newPSModulePathStr # Out-Log -Data "Added '$appxCmdletsPath' to PSModulePath." -Level 1 } else { # No PSModulePath registry key/value exists, so don't worry about modifying it. Just create a new one. Out-Log -Data (& setx /s $env:COMPUTERNAME PSModulePath $appxCmdletsPath /m 2>&1) -Level 3 $env:PSModulePath = $appxCmdletsPath # Out-Log -Data "Added '$appxCmdletsPath' to PSModulePath." -Level 1 } } Function Set-WinRMConfiguration { Param ( [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] $SetWinRMConfigurationResponse ) if ($SetWinRMConfigurationResponse) { Out-Log -Data (& winrm set winrm/config/client '@{AllowUnencrypted="true"}' 2>&1) -Level 3 Out-Log -Data (& winrm set winrm/config/client '@{TrustedHosts="*"}' 2>&1) -Level 3 Out-Log -Data 'WinRM successfully configured.' -Level 1 } } Function Test-PowerShellVersion { # This function tests the version of PowerShell / Windows Management Framework and prompts the user # to download a newer version if necessary. if ($PSVersionTable.PSVersion.Major -ge 4) { return } Out-Log -Data 'Failed to install FC REST API PowerKit: Windows Management Framework 4.0+ required.' -ForegroundColor Red -Level 0 Out-Log -Data ' Please install WMF 4.0 or higher and run this script again.' -ForegroundColor Red -Level 0 Out-Log -Data ' URL for WMF 4.0: https://www.microsoft.com/en-us/download/details.aspx?id=40855' -ForegroundColor Red -Level 0 Out-Log -Data ' URL for WMF 5.0: https://www.microsoft.com/en-us/download/details.aspx?id=50395' -ForegroundColor Red -NoNewline -Level 0 $title = 'Download Windows Management Framework 4.0+' $message = 'Windows Management Framework 4.0+ is required for FC REST API PowerKit. Which WMF version would you like to download?' $4 = New-Object System.Management.Automation.Host.ChoiceDescription '&4.0', ` 'Windows Management Framework 4.0 (https://www.microsoft.com/en-us/download/details.aspx?id=40855)' $5 = New-Object System.Management.Automation.Host.ChoiceDescription '&5.0', ` 'Windows Management Framework 5.0 (https://www.microsoft.com/en-us/download/details.aspx?id=50395)' $none = New-Object System.Management.Automation.Host.ChoiceDescription '&None', ` 'None' $options = [System.Management.Automation.Host.ChoiceDescription[]]($4, $5, $none) $tempResult = $host.ui.PromptForChoice($title, $message, $options, 1) if ($tempResult -eq 0) { Start-Process 'https://www.microsoft.com/en-us/download/details.aspx?id=40855' } elseif ($tempResult -eq 1) { Start-Process 'https://www.microsoft.com/en-us/download/details.aspx?id=50395' } # TODO - Add script exiting message. Exit } Function Test-RESTAPI { Param ( [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] $Url, [Parameter(Mandatory=$false)] [AllowNull()] $EncodedAuthString ) try { if ($encodedAuthString -eq $null) { $response = (Invoke-RestMethod -Method Get -Uri $Url).value } else { $response = (Invoke-RestMethod -Method Get -Headers @{'Authorization' = "Basic $EncodedAuthString"} -Uri $Url).value } $response = ($response | Out-String).Trim() Out-Log -Data "`n$response`n" -Level 2 } catch { $Global:errRest = $_ Out-Log -Data "REST API test failed" -ForegroundColor Red -Level 0 Out-Log -Data " StatusCode: $($_.Exception.Response.StatusCode.value__)" -ForegroundColor Red -Level 0 Out-Log -Data " StatusDescription: $($_.Exception.Response.StatusDescription)" -ForegroundColor Red -Level 0 } } Function Import-AllCmdlets { # This function imports all QLGCFC cmdlets into the current PowerShell session. # This function is necessary to update any cmdlet and cmdlet help changes that have occurred from one PowerKit # version to another. One consequence of force importing the QLGCFC cmdlets is the user will be prompted if they # want to run software from an unpublished publisher when this function is called rather than the first time # intellisense is invoked after the PowerKit is installed. Note that this prompt only occurs if it is the user's # first time installing the PowerKit. Of course, in this scenario this function call is not necessary, since the # cmdlets will not need to be refreshed. # TODO - Determine if this is the first time PowerKit has been installed / if user already trusts QLGC publisher. #$moduleNames = (Get-Module -ListAvailable | Where-Object { $_.Name.StartsWith('QLGCFC_') }).Name #foreach ($moduleName in $moduleNames) #{ # try # { # Import-Module `"C:\Program Files\WindowsPowerShell\Modules\MarvellQLogicFCCmdlets\MarvellQLogicFCCmdlets.psd1`" -Force # #2>&1 | Out-Null # } # catch # { # # ignore any exception when importing. ErrorAction and 2>&1 | Out-Null in above command does not work! # # Out-Log -Data " Error occured while performing : Import-Module $moduleName -Force" -ForegroundColor Red -Level 0 # } #} } Function Get-LocalAgentPropertiesFilePath { $retFilePath = $null $filePath = $null $appxPkg = $null $localInstallPath = $null $agentPropertiesFileName = "agent.properties" $appxPkg = Get-AppxPackageWrapper -AppxName 'MRVLFCProvider' $localInstallPath = $appxPkg.InstallLocation $localInstallPath = $localInstallPath.Trim(' ', '"') $filePath = $localInstallPath + "\" + $agentPropertiesFileName if (Test-Path -Path $filePath) { $retFilePath = $filePath } return $retFilePath } Function Set-AdministratorsPrivilegesOnLocalAgentPropertiesFilePath { $ret = $false $functionName = $MyInvocation.InvocationName $filePath = Get-LocalAgentPropertiesFilePath if (-not([String]::IsNullOrEmpty($filePath))) { $Group = New-Object System.Security.Principal.NTAccount("Builtin", "Administrators") if ($null -ne $Group) { $AccessRule = New-Object System.Security.AccessControl.FileSystemAccessRule($Group, "FullControl", "Allow") if ($null -ne $AccessRule) { $ACL = Get-ACL $filePath if ($null -ne $ACL) { $ACL.SetOwner($Group) $ACL.SetAccessRule($AccessRule) Set-Acl -Path $filePath -AclObject $ACL $ret = $true } } } } else { Out-Log -Data "[$functionName]: Cmdlet 'Get-LocalAgentPropertiesFilePath' failed to get local 'agent.properties' file path." -Level 3 } if ($false -eq $ret) { Out-Log -Data "[$functionName]: Failed to configure required administrators privileges on file[$filePath]" -ForegroundColor Red -Level 3 } else { Out-Log -Data "Administrators privileges configured successfully on file[agent.properties]." -Level 3 } } Function Set-VmwarePowerCLIPrompt { $title = 'Installation of module VMware.VimAutomation.Core' $message = 'VMware.VimAutomation.Core is required to communicate with ESXi8.0 U2 for Advanced Authentication.' $yes = New-Object System.Management.Automation.Host.ChoiceDescription '&Yes', ` 'Proceeding to install VMware.VimAutomation.Core.' $no = New-Object System.Management.Automation.Host.ChoiceDescription '&No', ` 'Communication to ESXi8.0 U2 cannot be done with Advanced Authentication.' $options = [System.Management.Automation.Host.ChoiceDescription[]]($yes, $no) $tempResult = $host.ui.PromptForChoice($title, $message, $options, 0) if ($tempResult -eq 0) { return $true } else { return $false } } Function Install-VMwarePowerCli { if(Get-Module -ListAvailable -Name "VMware.VimAutomation.Core") { Out-Log -Data "VMware.VimAutomation.Core is already installed.`n" -Level 0 } else { $response = Set-VmwarePowerCLIPrompt if($response) { Install-Module -Name VMware.VimAutomation.Core -Allowclobber if(Get-Module -ListAvailable -Name "VMware.Vim") { if(Get-Module -ListAvailable -Name "VMware.VimAutomation.Cis.Core") { if(Get-Module -ListAvailable -Name "VMware.VimAutomation.Common") { if(Get-Module -ListAvailable -Name "VMware.VimAutomation.Core") { if(Get-Module -ListAvailable -Name "VMware.VimAutomation.Sdk") { Add-ManyToRegistryWriteOver -Path "HKLM:\Software\Classes\CLSID\$Script:VMCORE_INSTALLED" -NameTypeValueArray @( @('(Default)', 'String', 'FC_POWERKIT_INSTALLED_VMCORE') ) Out-Log -Data "Successfully installated module VMware.VimAutomation.Core `n" -Level 0 } else { Out-Log -Data "Failed to installat module VMware.VimAutomation.Sdk `n" -ForegroundColor Red Exit } } else { Out-Log -Data "Failed to installat module VMware.VimAutomation.Core `n" -ForegroundColor Red Exit } } else { Out-Log -Data "Failed to installat module VMware.VimAutomation.Common `n" -ForegroundColor Red Exit } } else { Out-Log -Data "Failed to installat module VMware.VimAutomation.Cis.Core `n" -ForegroundColor Red Exit } } else { Out-Log -Data "Failed to installat module VMware.Vim `n" -ForegroundColor Red Exit } } } } Function Install-FCPowerKit { Param ( $response0, $response1, $response2 ) # Encapulate installer logic here #-------------------------------------------------------------------------------------------------- # Globals $Script:CLSID = '{A31B8A5E-8B6A-421C-8BB1-F85C9C34E659}' $Script:VMCORE_INSTALLED = '{D3624213-6190-4875-9d79-283941a91792}' $Script:savedAppxPolicyRegValData = $null $cmdletsAppxFileName = Get-CmdletsAppxFileName # $restAppxFileName = '.\MRVLFCProviderREST-WindowsServer.appx' $Script:appcmd = "$env:SystemRoot\system32\inetsrv\appcmd.exe" $Script:netsh = "$env:windir\system32\netsh.exe" $Script:Verbose = 1 #-------------------------------------------------------------------------------------------------- # User Input Test-ExecutionPolicy if ((Get-AppxPackage -Name 'MRVLFCProvider') -ne $null) { Test-LastExitCode -ExitCode 1 -Message "Could not install PowerKit because it is already installed! Please uninstall the PowerKit first." -ExitScriptIfBadErrorCode } else { $response0 = Set-WinRMConfigurationPrompt #$response1 = Install-PowerShellODataPrompt #$response2 = Add-AccountToIISPrompt -InstallPSODataResponse $response1 #$response0 = $WinRMConfiguration #$response1 = $Configure_IIS_OData_RESTAPI #$response2 = $AddAccountToIIS #$cred = $Credential #-------------------------------------------------------------------------------------------------- # Script - Cmdlets $Script:CurrentInstallation = 'MRVL FC PowerKit' Add-AppxTrustedAppsRegistryKey Remove-CLSIDRegistryPaths Add-AppxPackageWrapper -AppxFileName $cmdletsAppxFileName Add-CLSIDRegistryPathsAndMofCompInstall Remove-AppxTrustedAppsRegistryKey Copy-DependentLibsAndFilesToWbemPath Copy-CmdletsToProgramData Add-CmdletsPathToPSModulePath Set-AdministratorsPrivilegesOnLocalAgentPropertiesFilePath Install-VMwarePowerCli Set-WinRMConfiguration -SetWinRMConfigurationResponse $response0 Out-Log -Data "Successfully installed MarvellQLogicFCPowerKit.`n" -ForegroundColor Green -Level 0 #-------------------------------------------------------------------------------------------------- #Import-AllCmdlets Out-Log -Data "Note: if this was an upgrade from a previous version, you may need to restart the system for the new provider to be loaded." -ForegroundColor Yellow -Level 0 } } Export-ModuleMember -function Install-FCPowerKit # SIG # Begin signature block # MIIo6AYJKoZIhvcNAQcCoIIo2TCCKNUCAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCBEOKUJpVHzsTlv # 8kvrLM5na58Hi6fvJDTQBggO5FinZKCCDbUwggawMIIEmKADAgECAhAIrUCyYNKc # TJ9ezam9k67ZMA0GCSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQK # EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNV # BAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDAeFw0yMTA0MjkwMDAwMDBaFw0z # NjA0MjgyMzU5NTlaMGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwg # SW5jLjFBMD8GA1UEAxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcg # UlNBNDA5NiBTSEEzODQgMjAyMSBDQTEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw # ggIKAoICAQDVtC9C0CiteLdd1TlZG7GIQvUzjOs9gZdwxbvEhSYwn6SOaNhc9es0 # JAfhS0/TeEP0F9ce2vnS1WcaUk8OoVf8iJnBkcyBAz5NcCRks43iCH00fUyAVxJr # Q5qZ8sU7H/Lvy0daE6ZMswEgJfMQ04uy+wjwiuCdCcBlp/qYgEk1hz1RGeiQIXhF # LqGfLOEYwhrMxe6TSXBCMo/7xuoc82VokaJNTIIRSFJo3hC9FFdd6BgTZcV/sk+F # LEikVoQ11vkunKoAFdE3/hoGlMJ8yOobMubKwvSnowMOdKWvObarYBLj6Na59zHh # 3K3kGKDYwSNHR7OhD26jq22YBoMbt2pnLdK9RBqSEIGPsDsJ18ebMlrC/2pgVItJ # wZPt4bRc4G/rJvmM1bL5OBDm6s6R9b7T+2+TYTRcvJNFKIM2KmYoX7BzzosmJQay # g9Rc9hUZTO1i4F4z8ujo7AqnsAMrkbI2eb73rQgedaZlzLvjSFDzd5Ea/ttQokbI # YViY9XwCFjyDKK05huzUtw1T0PhH5nUwjewwk3YUpltLXXRhTT8SkXbev1jLchAp # QfDVxW0mdmgRQRNYmtwmKwH0iU1Z23jPgUo+QEdfyYFQc4UQIyFZYIpkVMHMIRro # OBl8ZhzNeDhFMJlP/2NPTLuqDQhTQXxYPUez+rbsjDIJAsxsPAxWEQIDAQABo4IB # WTCCAVUwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUaDfg67Y7+F8Rhvv+ # YXsIiGX0TkIwHwYDVR0jBBgwFoAU7NfjgtJxXWRM3y5nP+e6mK4cD08wDgYDVR0P # AQH/BAQDAgGGMBMGA1UdJQQMMAoGCCsGAQUFBwMDMHcGCCsGAQUFBwEBBGswaTAk # BggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEEGCCsGAQUFBzAC # hjVodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9v # dEc0LmNydDBDBgNVHR8EPDA6MDigNqA0hjJodHRwOi8vY3JsMy5kaWdpY2VydC5j # b20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNybDAcBgNVHSAEFTATMAcGBWeBDAED # MAgGBmeBDAEEATANBgkqhkiG9w0BAQwFAAOCAgEAOiNEPY0Idu6PvDqZ01bgAhql # +Eg08yy25nRm95RysQDKr2wwJxMSnpBEn0v9nqN8JtU3vDpdSG2V1T9J9Ce7FoFF # UP2cvbaF4HZ+N3HLIvdaqpDP9ZNq4+sg0dVQeYiaiorBtr2hSBh+3NiAGhEZGM1h # mYFW9snjdufE5BtfQ/g+lP92OT2e1JnPSt0o618moZVYSNUa/tcnP/2Q0XaG3Ryw # YFzzDaju4ImhvTnhOE7abrs2nfvlIVNaw8rpavGiPttDuDPITzgUkpn13c5Ubdld # AhQfQDN8A+KVssIhdXNSy0bYxDQcoqVLjc1vdjcshT8azibpGL6QB7BDf5WIIIJw # 8MzK7/0pNVwfiThV9zeKiwmhywvpMRr/LhlcOXHhvpynCgbWJme3kuZOX956rEnP # LqR0kq3bPKSchh/jwVYbKyP/j7XqiHtwa+aguv06P0WmxOgWkVKLQcBIhEuWTatE # QOON8BUozu3xGFYHKi8QxAwIZDwzj64ojDzLj4gLDb879M4ee47vtevLt/B3E+bn # KD+sEq6lLyJsQfmCXBVmzGwOysWGw/YmMwwHS6DTBwJqakAwSEs0qFEgu60bhQji # WQ1tygVQK+pKHJ6l/aCnHwZ05/LWUpD9r4VIIflXO7ScA+2GRfS0YW6/aOImYIbq # yK+p/pQd52MbOoZWeE4wggb9MIIE5aADAgECAhAEYJdqHDbejs1cXzHJ6L8yMA0G # CSqGSIb3DQEBCwUAMGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwg # SW5jLjFBMD8GA1UEAxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcg # UlNBNDA5NiBTSEEzODQgMjAyMSBDQTEwHhcNMjUwODE5MDAwMDAwWhcNMjgwODE4 # MjM1OTU5WjCBhDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFDAS # BgNVBAcTC1NhbnRhIENsYXJhMSQwIgYDVQQKExtNYXJ2ZWxsIFNlbWljb25kdWN0 # b3IsIEluYy4xJDAiBgNVBAMTG01hcnZlbGwgU2VtaWNvbmR1Y3RvciwgSW5jLjCC # AaIwDQYJKoZIhvcNAQEBBQADggGPADCCAYoCggGBALgA/cizJlupUPUg6i79vNcX # i+/YfRBBwY3CKDlW1u4PeQm0TNUsJK0+5b9rINoomB7vv6QRUEHyuuxe843CLH41 # pbOuI3o9l+xwxVRJabCafXoE5ZucAhoPoNiDFcfmuPUpiBlaxh7Sd440Cl8kcagN # fgdTNbB5SiKNOL1t8hVA4pyr7dAA9pa+QyvyjmIs+CJ65Su5uQ9+Q7Vg5W/ZNTF+ # nOCeFStbTGr92jNhzElKYA6RE027z8vXhnEovU/1WhzdGR7eA5ftIrnn8vyGjw3K # 7CadB7SmuivYXv8q1324L88uwnRvU/MQ0DOZZtOJYA3+/lgbhVU4NCuhSOR3vGSp # AzmEQ6l394pkUF8evS74U0UIO2ZL2a3xz6cLEj5NUYmkkHxPeru61VaqfyUDmJpo # iJkW+MSOaWAj6SdlqNrgXu6hxs/PHWUNkQNPQxiA2ycBPtYUSHkar3HPXLKtnzLR # KdcCdpAlw/fRNux8jXE9SPyMDFHhhNUDg2k5brPzzwIDAQABo4ICAzCCAf8wHwYD # VR0jBBgwFoAUaDfg67Y7+F8Rhvv+YXsIiGX0TkIwHQYDVR0OBBYEFO+n3VfxBXU9 # 6GYBaEVfXxxHBAxLMD4GA1UdIAQ3MDUwMwYGZ4EMAQQBMCkwJwYIKwYBBQUHAgEW # G2h0dHA6Ly93d3cuZGlnaWNlcnQuY29tL0NQUzAOBgNVHQ8BAf8EBAMCB4AwEwYD # VR0lBAwwCgYIKwYBBQUHAwMwgbUGA1UdHwSBrTCBqjBToFGgT4ZNaHR0cDovL2Ny # bDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZEc0Q29kZVNpZ25pbmdSU0E0 # MDk2U0hBMzg0MjAyMUNBMS5jcmwwU6BRoE+GTWh0dHA6Ly9jcmw0LmRpZ2ljZXJ0 # LmNvbS9EaWdpQ2VydFRydXN0ZWRHNENvZGVTaWduaW5nUlNBNDA5NlNIQTM4NDIw # MjFDQTEuY3JsMIGUBggrBgEFBQcBAQSBhzCBhDAkBggrBgEFBQcwAYYYaHR0cDov # L29jc3AuZGlnaWNlcnQuY29tMFwGCCsGAQUFBzAChlBodHRwOi8vY2FjZXJ0cy5k # aWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRDb2RlU2lnbmluZ1JTQTQwOTZT # SEEzODQyMDIxQ0ExLmNydDAJBgNVHRMEAjAAMA0GCSqGSIb3DQEBCwUAA4ICAQCT # LeW1yzArgc7cgMfOlyWu4qmOmYOli1D4qVMxyxCv4RWOZF5c+Z19U4qufbX6qcB4 # Iv/I1TVhgIMLTeIOcSUO3Ft6gv6NMO0ur38i1C17JI1IGSwlY5xeLUHpZ33eRBD7 # RWGfAE/hFI0Nou4qagrFvbwfWLq27uXM75l3r9YjAz3Pq9pnetdncy++M7N8vlFo # yTR+vSSXlgRpdEmfT9DQa4rmw1BYs40Dx5yAtT2v+XC6JBEjTCUFb9JFQ2Q3r+kA # ruLeOlH9CcuFnMXllLUyEYzMko6Z//H0DVqY79Q3rCjcrVmpzLvUWWpoCC8ey/3R # d7StjHA/9UsKDpWqAl55kCGfOfv+s2KRQKYlZcJnTvB7TisNdifTwpEZvcDbbABU # HcOkW13tyayY606l4yX4xbE2lByiLVtb/zSvsL0OVjSkhNhXfAXdCryF2N455ycr # Ba8l7hiADBqEIGGwkM4pAyiA329ndNRRQr8TcOjz7tuzfmipj/0S7YZpEfyhwjyn # saFFnYSQi1LxZ9gTyMAPmxLpQV/KKuejgejPDFdXWqmKvc7oU+DE9m3pRBe4N+/R # L6tXuwSm4WWfS6feI6b49mTXQMf0Pg3lza46iuSYpq5Ilf7YJF1qrZnA9XH+fzvS # z98tnoxJkEMdZuBrwMHuRHJ4N6o9QJZaJi7sIq8BqzGCGokwghqFAgEBMH0waTEL # MAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMUEwPwYDVQQDEzhE # aWdpQ2VydCBUcnVzdGVkIEc0IENvZGUgU2lnbmluZyBSU0E0MDk2IFNIQTM4NCAy # MDIxIENBMQIQBGCXahw23o7NXF8xyei/MjANBglghkgBZQMEAgEFAKCB5DAZBgkq # hkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGC # NwIBFTAvBgkqhkiG9w0BCQQxIgQgC4/CZUm83A4TdBQclzdgUpS4KW+7MF9KjxvH # u2Ai6twweAYKKwYBBAGCNwIBDDFqMGigTIBKAE0AYQByAHYAZQBsAGwAIABRAEwA # bwBnAGkAYwAgAEYAaQBiAHIAZQAgAEMAaABhAG4AbgBlAGwAIABQAG8AdwBlAHIA # SwBpAHShGIAWaHR0cDovL3d3dy5tYXJ2ZWxsLmNvbTANBgkqhkiG9w0BAQEFAASC # AYCGpC7UnnbLo4nriSJ/lc9HotiG9nORzxgtaFC/okCnXlXUULBonxSWhxCw78cd # 4tE2UB2mBHyzRuAjWsbEKQ6v67ECJ2Ym2HKcTF9x6zUIZKgeLsiE7LtskV6rLM0T # 0/y2jJwBZH9vXaAJ/SBVj6dRjf6SMtlpvxNWm8CbuAnnkwFHNDguKJR2bOmoyqic # 9grWbwzaabJnx5ZS4A+EEnzNnz0r7HKwpk7SyOn/rywzEohRmOQyzW4qBshHrX8T # B95OOrIFugNQhaQwFAUOZkDDMon3wD5ciG8JyJWWoiDb69WRu9nlfPavVaphHF4V # 8AqfuN8a/94G0dEfEtlz6qVh3GogvfhL18Zsq0rH3rsk/i85LGpQ0x21CxJ1Hfse # w9xofunHPHVvWUustcu/3Pc6GtWZgEV1Jtzjy9s3bLjY9Cn9Ir4S/VFkfd0huxGS # 3Pg4zJywUU7CTlq/HAeZxYVKmzRVousgg8m5MfU+13yqnJp2R2PDyYKXqn/OPDML # ZzKhghd2MIIXcgYKKwYBBAGCNwMDATGCF2IwghdeBgkqhkiG9w0BBwKgghdPMIIX # SwIBAzEPMA0GCWCGSAFlAwQCAQUAMHcGCyqGSIb3DQEJEAEEoGgEZjBkAgEBBglg # hkgBhv1sBwEwMTANBglghkgBZQMEAgEFAAQgFaidyXOzqe6luzXtycH05/tjMBph # P3bqW38t8Wx8YGECEG3xYG+Pps+AMsMA1DPZiDMYDzIwMjYwNzAxMDYyNzMyWqCC # EzowggbtMIIE1aADAgECAhAKgO8YS43xBYLRxHanlXRoMA0GCSqGSIb3DQEBCwUA # MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE # AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBUaW1lU3RhbXBpbmcgUlNBNDA5NiBTSEEy # NTYgMjAyNSBDQTEwHhcNMjUwNjA0MDAwMDAwWhcNMzYwOTAzMjM1OTU5WjBjMQsw # CQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xOzA5BgNVBAMTMkRp # Z2lDZXJ0IFNIQTI1NiBSU0E0MDk2IFRpbWVzdGFtcCBSZXNwb25kZXIgMjAyNSAx # MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA0EasLRLGntDqrmBWsytX # um9R/4ZwCgHfyjfMGUIwYzKomd8U1nH7C8Dr0cVMF3BsfAFI54um8+dnxk36+jx0 # Tb+k+87H9WPxNyFPJIDZHhAqlUPt281mHrBbZHqRK71Em3/hCGC5KyyneqiZ7syv # FXJ9A72wzHpkBaMUNg7MOLxI6E9RaUueHTQKWXymOtRwJXcrcTTPPT2V1D/+cFll # ESviH8YjoPFvZSjKs3SKO1QNUdFd2adw44wDcKgH+JRJE5Qg0NP3yiSyi5MxgU6c # ehGHr7zou1znOM8odbkqoK+lJ25LCHBSai25CFyD23DZgPfDrJJJK77epTwMP6eK # A0kWa3osAe8fcpK40uhktzUd/Yk0xUvhDU6lvJukx7jphx40DQt82yepyekl4i0r # 8OEps/FNO4ahfvAk12hE5FVs9HVVWcO5J4dVmVzix4A77p3awLbr89A90/nWGjXM # Gn7FQhmSlIUDy9Z2hSgctaepZTd0ILIUbWuhKuAeNIeWrzHKYueMJtItnj2Q+aTy # LLKLM0MheP/9w6CtjuuVHJOVoIJ/DtpJRE7Ce7vMRHoRon4CWIvuiNN1Lk9Y+xZ6 # 6lazs2kKFSTnnkrT3pXWETTJkhd76CIDBbTRofOsNyEhzZtCGmnQigpFHti58CSm # vEyJcAlDVcKacJ+A9/z7eacCAwEAAaOCAZUwggGRMAwGA1UdEwEB/wQCMAAwHQYD # VR0OBBYEFOQ7/PIx7f391/ORcWMZUEPPYYzoMB8GA1UdIwQYMBaAFO9vU0rp5AZ8 # esrikFb2L9RJ7MtOMA4GA1UdDwEB/wQEAwIHgDAWBgNVHSUBAf8EDDAKBggrBgEF # BQcDCDCBlQYIKwYBBQUHAQEEgYgwgYUwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3Nw # LmRpZ2ljZXJ0LmNvbTBdBggrBgEFBQcwAoZRaHR0cDovL2NhY2VydHMuZGlnaWNl # cnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZEc0VGltZVN0YW1waW5nUlNBNDA5NlNIQTI1 # NjIwMjVDQTEuY3J0MF8GA1UdHwRYMFYwVKBSoFCGTmh0dHA6Ly9jcmwzLmRpZ2lj # ZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNFRpbWVTdGFtcGluZ1JTQTQwOTZTSEEy # NTYyMDI1Q0ExLmNybDAgBgNVHSAEGTAXMAgGBmeBDAEEAjALBglghkgBhv1sBwEw # DQYJKoZIhvcNAQELBQADggIBAGUqrfEcJwS5rmBB7NEIRJ5jQHIh+OT2Ik/bNYul # CrVvhREafBYF0RkP2AGr181o2YWPoSHz9iZEN/FPsLSTwVQWo2H62yGBvg7ouCOD # wrx6ULj6hYKqdT8wv2UV+Kbz/3ImZlJ7YXwBD9R0oU62PtgxOao872bOySCILdBg # hQ/ZLcdC8cbUUO75ZSpbh1oipOhcUT8lD8QAGB9lctZTTOJM3pHfKBAEcxQFoHlt # 2s9sXoxFizTeHihsQyfFg5fxUFEp7W42fNBVN4ueLaceRf9Cq9ec1v5iQMWTFQa0 # xNqItH3CPFTG7aEQJmmrJTV3Qhtfparz+BW60OiMEgV5GWoBy4RVPRwqxv7Mk0Sy # 4QHs7v9y69NBqycz0BZwhB9WOfOu/CIJnzkQTwtSSpGGhLdjnQ4eBpjtP+XB3pQC # tv4E5UCSDag6+iX8MmB10nfldPF9SVD7weCC3yXZi/uuhqdwkgVxuiMFzGVFwYbQ # siGnoa9F5AaAyBjFBtXVLcKtapnMG3VH3EmAp/jsJ3FVF3+d1SVDTmjFjLbNFZUW # MXuZyvgLfgyPehwJVxwC+UpX2MSey2ueIu9THFVkT+um1vshETaWyQo8gmBto/m3 # acaP9QsuLj3FNwFlTxq25+T4QwX9xa6ILs84ZPvmpovq90K8eWyG2N01c4IhSOxq # t81nMIIGtDCCBJygAwIBAgIQDcesVwX/IZkuQEMiDDpJhjANBgkqhkiG9w0BAQsF # ADBiMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQL # ExB3d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJv # b3QgRzQwHhcNMjUwNTA3MDAwMDAwWhcNMzgwMTE0MjM1OTU5WjBpMQswCQYDVQQG # EwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xQTA/BgNVBAMTOERpZ2lDZXJ0 # IFRydXN0ZWQgRzQgVGltZVN0YW1waW5nIFJTQTQwOTYgU0hBMjU2IDIwMjUgQ0Ex # MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAtHgx0wqYQXK+PEbAHKx1 # 26NGaHS0URedTa2NDZS1mZaDLFTtQ2oRjzUXMmxCqvkbsDpz4aH+qbxeLho8I6jY # 3xL1IusLopuW2qftJYJaDNs1+JH7Z+QdSKWM06qchUP+AbdJgMQB3h2DZ0Mal5kY # p77jYMVQXSZH++0trj6Ao+xh/AS7sQRuQL37QXbDhAktVJMQbzIBHYJBYgzWIjk8 # eDrYhXDEpKk7RdoX0M980EpLtlrNyHw0Xm+nt5pnYJU3Gmq6bNMI1I7Gb5IBZK4i # vbVCiZv7PNBYqHEpNVWC2ZQ8BbfnFRQVESYOszFI2Wv82wnJRfN20VRS3hpLgIR4 # hjzL0hpoYGk81coWJ+KdPvMvaB0WkE/2qHxJ0ucS638ZxqU14lDnki7CcoKCz6eu # m5A19WZQHkqUJfdkDjHkccpL6uoG8pbF0LJAQQZxst7VvwDDjAmSFTUms+wV/FbW # Bqi7fTJnjq3hj0XbQcd8hjj/q8d6ylgxCZSKi17yVp2NL+cnT6Toy+rN+nM8M7Ln # LqCrO2JP3oW//1sfuZDKiDEb1AQ8es9Xr/u6bDTnYCTKIsDq1BtmXUqEG1NqzJKS # 4kOmxkYp2WyODi7vQTCBZtVFJfVZ3j7OgWmnhFr4yUozZtqgPrHRVHhGNKlYzyjl # roPxul+bgIspzOwbtmsgY1MCAwEAAaOCAV0wggFZMBIGA1UdEwEB/wQIMAYBAf8C # AQAwHQYDVR0OBBYEFO9vU0rp5AZ8esrikFb2L9RJ7MtOMB8GA1UdIwQYMBaAFOzX # 44LScV1kTN8uZz/nupiuHA9PMA4GA1UdDwEB/wQEAwIBhjATBgNVHSUEDDAKBggr # BgEFBQcDCDB3BggrBgEFBQcBAQRrMGkwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3Nw # LmRpZ2ljZXJ0LmNvbTBBBggrBgEFBQcwAoY1aHR0cDovL2NhY2VydHMuZGlnaWNl # cnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZFJvb3RHNC5jcnQwQwYDVR0fBDwwOjA4oDag # NIYyaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZFJvb3RH # NC5jcmwwIAYDVR0gBBkwFzAIBgZngQwBBAIwCwYJYIZIAYb9bAcBMA0GCSqGSIb3 # DQEBCwUAA4ICAQAXzvsWgBz+Bz0RdnEwvb4LyLU0pn/N0IfFiBowf0/Dm1wGc/Do # 7oVMY2mhXZXjDNJQa8j00DNqhCT3t+s8G0iP5kvN2n7Jd2E4/iEIUBO41P5F448r # SYJ59Ib61eoalhnd6ywFLerycvZTAz40y8S4F3/a+Z1jEMK/DMm/axFSgoR8n6c3 # nuZB9BfBwAQYK9FHaoq2e26MHvVY9gCDA/JYsq7pGdogP8HRtrYfctSLANEBfHU1 # 6r3J05qX3kId+ZOczgj5kjatVB+NdADVZKON/gnZruMvNYY2o1f4MXRJDMdTSlOL # h0HCn2cQLwQCqjFbqrXuvTPSegOOzr4EWj7PtspIHBldNE2K9i697cvaiIo2p61E # d2p8xMJb82Yosn0z4y25xUbI7GIN/TpVfHIqQ6Ku/qjTY6hc3hsXMrS+U0yy+GWq # AXam4ToWd2UQ1KYT70kZjE4YtL8Pbzg0c1ugMZyZZd/BdHLiRu7hAWE6bTEm4XYR # kA6Tl4KSFLFk43esaUeqGkH/wyW4N7OigizwJWeukcyIPbAvjSabnf7+Pu0VrFgo # iovRDiyx3zEdmcif/sYQsfch28bZeUz2rtY/9TCA6TD8dC3JE3rYkrhLULy7Dc90 # G6e8BlqmyIjlgp2+VqsS9/wQD7yFylIz0scmbKvFoW2jNrbM1pD2T7m3XDCCBY0w # ggR1oAMCAQICEA6bGI750C3n79tQ4ghAGFowDQYJKoZIhvcNAQEMBQAwZTELMAkG # A1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRp # Z2ljZXJ0LmNvbTEkMCIGA1UEAxMbRGlnaUNlcnQgQXNzdXJlZCBJRCBSb290IENB # MB4XDTIyMDgwMTAwMDAwMFoXDTMxMTEwOTIzNTk1OVowYjELMAkGA1UEBhMCVVMx # FTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNv # bTEhMB8GA1UEAxMYRGlnaUNlcnQgVHJ1c3RlZCBSb290IEc0MIICIjANBgkqhkiG # 9w0BAQEFAAOCAg8AMIICCgKCAgEAv+aQc2jeu+RdSjwwIjBpM+zCpyUuySE98orY # WcLhKac9WKt2ms2uexuEDcQwH/MbpDgW61bGl20dq7J58soR0uRf1gU8Ug9SH8ae # FaV+vp+pVxZZVXKvaJNwwrK6dZlqczKU0RBEEC7fgvMHhOZ0O21x4i0MG+4g1ckg # HWMpLc7sXk7Ik/ghYZs06wXGXuxbGrzryc/NrDRAX7F6Zu53yEioZldXn1RYjgwr # t0+nMNlW7sp7XeOtyU9e5TXnMcvak17cjo+A2raRmECQecN4x7axxLVqGDgDEI3Y # 1DekLgV9iPWCPhCRcKtVgkEy19sEcypukQF8IUzUvK4bA3VdeGbZOjFEmjNAvwjX # WkmkwuapoGfdpCe8oU85tRFYF/ckXEaPZPfBaYh2mHY9WV1CdoeJl2l6SPDgohIb # Zpp0yt5LHucOY67m1O+SkjqePdwA5EUlibaaRBkrfsCUtNJhbesz2cXfSwQAzH0c # lcOP9yGyshG3u3/y1YxwLEFgqrFjGESVGnZifvaAsPvoZKYz0YkH4b235kOkGLim # dwHhD5QMIR2yVCkliWzlDlJRR3S+Jqy2QXXeeqxfjT/JvNNBERJb5RBQ6zHFynIW # IgnffEx1P2PsIV/EIFFrb7GrhotPwtZFX50g/KEexcCPorF+CiaZ9eRpL5gdLfXZ # qbId5RsCAwEAAaOCATowggE2MA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFOzX # 44LScV1kTN8uZz/nupiuHA9PMB8GA1UdIwQYMBaAFEXroq/0ksuCMS1Ri6enIZ3z # bcgPMA4GA1UdDwEB/wQEAwIBhjB5BggrBgEFBQcBAQRtMGswJAYIKwYBBQUHMAGG # GGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBDBggrBgEFBQcwAoY3aHR0cDovL2Nh # Y2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENBLmNydDBF # BgNVHR8EPjA8MDqgOKA2hjRodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNl # cnRBc3N1cmVkSURSb290Q0EuY3JsMBEGA1UdIAQKMAgwBgYEVR0gADANBgkqhkiG # 9w0BAQwFAAOCAQEAcKC/Q1xV5zhfoKN0Gz22Ftf3v1cHvZqsoYcs7IVeqRq7IviH # GmlUIu2kiHdtvRoU9BNKei8ttzjv9P+Aufih9/Jy3iS8UgPITtAq3votVs/59Pes # MHqai7Je1M/RQ0SbQyHrlnKhSLSZy51PpwYDE3cnRNTnf+hZqPC/Lwum6fI0POz3 # A8eHqNJMQBk1RmppVLC4oVaO7KTVPeix3P0c2PR3WlxUjG/voVA9/HYJaISfb8rb # II01YBwCA8sgsKxYoA5AY8WYIsGyWfVVa88nq2x2zm8jLfR+cWojayL/ErhULSd+ # 2DrZ8LaHlv1b0VysGMNNn3O3AamfV6peKOK5lDGCA3wwggN4AgEBMH0waTELMAkG # A1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMUEwPwYDVQQDEzhEaWdp # Q2VydCBUcnVzdGVkIEc0IFRpbWVTdGFtcGluZyBSU0E0MDk2IFNIQTI1NiAyMDI1 # IENBMQIQCoDvGEuN8QWC0cR2p5V0aDANBglghkgBZQMEAgEFAKCB0TAaBgkqhkiG # 9w0BCQMxDQYLKoZIhvcNAQkQAQQwHAYJKoZIhvcNAQkFMQ8XDTI2MDcwMTA2Mjcz # MlowKwYLKoZIhvcNAQkQAgwxHDAaMBgwFgQU3WIwrIYKLTBr2jixaHlSMAf7QX4w # LwYJKoZIhvcNAQkEMSIEIIhYxbAItG/mGYALNifHTOWK2u4uFruGZu9fAnW11/Ra # MDcGCyqGSIb3DQEJEAIvMSgwJjAkMCIEIEqgP6Is11yExVyTj4KOZ2ucrsqzP+Nt # JpqjNPFGEQozMA0GCSqGSIb3DQEBAQUABIICALl6MT9PZByCd89Okq4CUgoBOcW2 # xNlIFe1dPd+CcUiPbYFzLguw5nO49XGJawi+ErlK//nGo8GZ9RTfmFVHeGxilA3h # O/8F7QZFgMKmwCAOCpjxBZ7+eLj9Kutt3nMDROMq31MDu5jChqdXa347SdqxPLNL # npQH+SlyLMGGmAF1E2btig8cPwJhvY9qhjurm57wggWTvJfFyRQIw0bKnrFBxcId # IuRiLmY0INWkz3o93/+jUzNAuT3UJ9y6ElUNJtqQYM1PyXz6Y6DRCXVmjkefU5TS # UPfIuDGZSkDWayBkM3Wer06Nx7RpaY6BagucsPMtGgMLQSAZiW5wqiAsT+FQQghc # xRbiAJ5dPFtc0rx2VpS6ER4Ax8A4ame4rpsTjnBCEJlYXp4Y4fr9lEPc4GMV2HNP # PUWpEsdKg0R43GpPGRqitTz8EycJOwROk7Vj8/RkJgQhaZkrrTSdJhvESqsmI6VT # BDXDu/1LBBg4UhVRjk/HGI9VjphAdHjvI0Ol7Wab+IDHY+LzcYSwxAeZUwx8qLSC # Fz/Abzgwbbt1tAWufQM8Gk6NAZfjSR7AEUpC/OtLEACYK+O5zfZQ4hEhs6fuVvfM # qoI6pUeQlQKCC8Ti/auqvjiUTdneVvIqWk1U/9a7ePqM1k0v+W29zpDh0H5UNBkE # PMp6zEnlER56sD1c # SIG # End signature block |