InstallMarvellQLogicFCPowerKit.psm1
#/************************************************************************ #* * #* NOTICE * #* * #* COPYRIGHT 2019-2022 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. * #* * #************************************************************************/ # Script level parameters Param ( [Switch] $ConfigureWinRM = $false, # For silent WinRM configuration [Switch] $AddIISAccount = $false, # Avoids a prompt - still need to enter username and password though. [Switch] $SkipEnablingWindowsFeaturesDEBUG = $false ) # 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 Get-CmdletsAppxFileName { 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 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=$true)] [ValidateNotNullOrEmpty()] $SuccessMessage, [Parameter(Mandatory=$true)] [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 $Script:CurrentInstallation." -ForegroundColor Red -Level 0 Exit } # Prompts Function Set-WinRMConfigurationPrompt { 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 } } # 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. # 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 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 { $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 } # Copy cmdlets to ProgramData $appxPkg = Get-AppxPackageWrapper -AppxName 'MRVLFCProvider' $cmdletsPath = $appxPkg.InstallLocation + '\Cmdlets\' if (Test-Path $cmdletsPath) { $cmdletsDestPath = "$env:ProgramData\$($appxPkg.PackageFullName)" Invoke-IOWrapper -Expression "Copy-Item -Path `"$cmdletsPath`" -Destination $cmdletsDestPath -Recurse -Force" ` -SuccessMessage "Cmdlets copied to '$cmdletsDestPath'." ` -FailMessage "Failed to copy FC cmdlets." } } Function Add-CmdletsPathToPSModulePath { #$p = [Environment]::GetEnvironmentVariable("PSModulePath") #Out-Log -Data "PSModulePath = '$p'" -Level 1 # This function adds cmdlet module path to system PSModulePath environment variable permanently. # Out-Log -Data "PSModulePath = '$appxCmdletsPath' to PSModulePath." -Level 1 $appxCmdletsPath = (Get-AppxPackageWrapper -AppxName 'MRVLFCProvider').InstallLocation + '\Cmdlets' if (Test-RegistryValue -Path 'HKLM:\System\CurrentControlSet\Control\Session Manager\Environment' -Value 'PSModulePath') { $currPSModulePathArr = [Environment]::GetEnvironmentVariable("PSModulePath").Split(';') #Out-Log -Data "currPSModulePathArr '$currPSModulePathArr'" -Level 1 $newPSModulePathArr = @() # Remove any old paths with MRVLFCProvider from PSModulePath foreach ($path in $currPSModulePathArr) { if (-not $path.Contains('MRVLFCProvider')) { # Out-Log -Data "path '$path'" -Level 1 $newPSModulePathArr += $path } } # Add new cmdlet path to PSModulePath $newPSModulePathArr += $appxCmdletsPath # Skip for uninstall script $newPSModulePathStr = $newPSModulePathArr -join ';' #Out-Log -Data "new PSModulePath '$newPSModulePathArr'" -Level 1 # 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 FastLinq 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 FastLinq 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 Enable-WindowsFeatures { # This function enables the Windows features required for REST API to work. # This function does not check if the Windows feature is already enabled or not, # but enabling an already-enabled feature is not harmful. if ( $SkipEnablingWindowsFeaturesDEBUG) { return } Out-Log -Data 'Installing Management OData Service...' -Level 1 $allFeatureNames = 'ManagementOdata, WCF-HTTP-Activation45, IIS-BasicAuthentication, IIS-WindowsAuthentication, IIS-WebServerManagementTools, IIS-ManagementConsole, IIS-ManagementScriptingTools, IIS-ManagementService, IIS-IIS6ManagementCompatibility, IIS-Metabase, IIS-WMICompatibility, IIS-LegacyScripts, IIS-LegacySnapIn' -split ', ' for ($i=0; $i -lt $allFeatureNames.Count; $i++) { $featureName = $allFeatureNames[$i] Write-Progress -Activity 'Enabling required Windows features' -Status "Enabling $featureName ($($i+1) of $($allFeatureNames.Count))" -PercentComplete ($i / $allFeatureNames.Count * 100) Out-Log -Data (Enable-WindowsOptionalFeature -Online -FeatureName $featureName -All -NoRestart 2>&1) -Level 3 } Write-Progress -Activity "Completed" -Completed } 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 adapters_cna_provider.properties to "C:\Windows\System32\wbem" Invoke-IOWrapper -Expression "Copy-Item -Path `"$dependentLibPath\adapters_cna_provider.properties`" -Destination `"$dependentLibDestPath`" -Recurse -Force" ` -SuccessMessage "Copied dependent file - adapters_cna_provider.properties to Wbem path : '$dependentLibDestPath'." ` -FailMessage "Failed to copy dependent file - adapters_cna_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 Install-WebServiceEndPoint { # This function configures IIS and sets up the web service endpoint and is an adaptation of the # 'SetupIISConfig.ps1' from the 'Windows Powershell role-based OData Web Service sample' project. # This is a massive function containing several helper functions within it. Param ( [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] $Site, [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] $Path, [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] $Cfgfile, [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] $Port, [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] $App, [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] $AppPool, [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] $Svc, [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] $Schema, [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] $DispatchXml, [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] $Rbac, [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] $CustomPluginAssembly ) Function Backup-IISConfig { $dt = Get-Date $backupID = "MODataIISSetup_$($dt.Year)-$($dt.Month)-$($dt.Day)-$($dt.Hour)-$($dt.Minute)-$($dt.Second)" Out-Log -Data (& $Script:appcmd add backup $backupID 2>&1) -Level 2 } Function Copy-FilesToCorrectLocation { if (-not (Test-Path $Path)) { Out-Log -Data (New-Item -Path $Path -ItemType Container 2>&1) -Level 3 } Copy-Item -Path $Cfgfile -Destination (Join-Path $Path 'web.config') Copy-Item -Path $Svc -Destination $Path Copy-Item -Path $Schema -Destination $Path Copy-Item -Path $DispatchXml -Destination $Path Copy-Item -Path $Rbac -Destination (Join-Path $Path 'RbacConfiguration.xml') Copy-Item -Path $CustomPluginAssembly -Destination $Path } Function Invoke-ActionAllSites { Param ( [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] $Action ) $siteInfoArray = (& $Script:appcmd list site) if ($siteInfoArray -eq $null) { return } foreach ($siteInfo in $siteInfoArray.Split("`n")) { $siteName = $siteInfo.split('"')[1] Invoke-ActionOneSite -SiteAction $Action -SiteName $siteName } } Function Invoke-ActionOneSite { Param ( [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] $SiteAction, [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] $SiteName ) Out-Log -Data (& $Script:appcmd $SiteAction site $SiteName 2>&1) -Level 2 } Function New-SiteID { # Determine an unused ID number to use. $siteInfoArray = (& $Script:appcmd list site) if ($siteInfoArray -eq $null) { return 1 } $largestId = 0 foreach ($siteInfo in $siteInfoArray.Split("`n")) { $i = $siteInfo.IndexOf('id:') + 3 $c = $siteInfo.IndexOf(',', $i) - $i $id = $siteInfo.Substring($i, $c) if ($id -gt $largestId) { $largestId = $id } } return [int]$largestId + 1 } Function Unlock-AuthSections { Out-Log -Data (& $Script:appcmd unlock config -section:access 2>&1) -Level 2 Out-Log -Data (& $Script:appcmd unlock config -section:anonymousAuthentication 2>&1) -Level 2 Out-Log -Data (& $Script:appcmd unlock config -section:basicAuthentication 2>&1) -Level 2 Out-Log -Data (& $Script:appcmd unlock config -section:windowsAuthentication 2>&1) -Level 2 } Function Set-RequiredAuthentication { Param ( [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] $EffVdir ) Out-Log -Data (& $Script:appcmd set config $EffVdir /section:system.webServer/security/authentication/anonymousAuthentication /enabled:false /commit:apphost 2>&1) -Level 2 Out-Log -Data (& $Script:appcmd set config $EffVdir /section:system.webServer/security/authentication/basicAuthentication /enabled:true /commit:apphost 2>&1) -Level 2 Out-Log -Data (& $Script:appcmd set config $EffVdir /section:system.webServer/security/authentication/windowsAuthentication /enabled:true /commit:apphost 2>&1) -Level 2 } Function New-FirewallRule { Param ( [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] $FirewallPort ) # Remove any existing firewall rules if ((Get-NetFirewallRule | Where-Object { ($_.DisplayName).StartsWith('MOData_IIS_Port') }) -ne $null) { Out-Log -Data (Remove-NetFirewallRule -DisplayName 'MOData_IIS_Port') -Level 3 } # Add new firewall rule Out-Log -Data (New-NetFirewallRule -DisplayName 'MOData_IIS_Port' -Direction Inbound -Action Allow -Protocol TCP -LocalPort $FirewallPort) -Level 3 } # TODO Check if all the params above which are files exist before continuing # TODO Check if IIS is installed before continuing Out-Log -Data 'Setting up web service endpoint ...' -Level 1 $appxPkg = Get-AppxPackageWrapper -AppxName 'MRVLFCProviderREST' $restFilesPath = "$env:ProgramData\$($appxPkg.PackageFullName)" Push-Location -Path $restFilesPath Out-Log -Data (& $Script:appcmd delete apppool MOData 2>&1) -Level 2 Out-Log -Data (& $Script:appcmd delete site $Site 2>&1) -Level 2 Backup-IISConfig Copy-FilesToCorrectLocation Invoke-ActionAllSites -Action 'Stop' Out-Log -Data (& $Script:appcmd stop apppool /apppool.name:DefaultAppPool 2>&1) -Level 2 Out-Log -Data (& $Script:appcmd start apppool /apppool.name:DefaultAppPool 2>&1) -Level 2 $siteID = New-SiteID Out-Log -Data (& $Script:appcmd add apppool /name:$AppPool 2>&1) -Level 2 Out-Log -Data (& $Script:appcmd set apppool /apppool.name:$AppPool /managedRuntimeVersion:"v4.0" 2>&1) -Level 2 Out-Log -Data (& $Script:appcmd add site /name:$Site /id:$siteID /bindings:http://*:$Port /physicalPath:$env:SystemDrive\inetpub\wwwroot 2>&1) -Level 2 Out-Log -Data (& $Script:appcmd set site /site.name:$Site /[path=`'/`'].applicationPool:$AppPool 2>&1) -Level 2 Out-Log -Data (& $Script:appcmd delete app $site/$app 2>&1) -Level 2 Out-Log -Data (& $Script:appcmd add app /site.name:$site /path:/$app /physicalPath:$path 2>&1) -Level 2 Out-Log -Data (& $Script:appcmd set app /app.name:$site/$app /applicationPool:$AppPool 2>&1) -Level 2 Unlock-AuthSections $effVdir = "$site" Set-RequiredAuthentication -EffVdir $effVdir Invoke-ActionOneSite -SiteAction 'Start' -SiteName $Site New-FirewallRule -FirewallPort $port Invoke-ActionAllSites -Action 'Start' Out-Log -Data 'Web Service endpoint is setup. The source root URI is:' -Level 0 Out-Log -Data " http://localhost:$Port/$Site/$($Svc.Substring(2, $Svc.Length - 2))/" -Level 0 -ForegroundColor Gray Pop-Location Start-Sleep -Seconds 5 } Function Add-AccountToIIS { Param ( [Parameter(Mandatory=$true)] [AllowNull()] $Credential ) if ($Credential -eq $null) { Out-Log -Data "No account added to IIS.`n" -Level 1 return } Out-Log -Data (& $Script:appcmd set config MODataSvc/MODataSvc -section:system.web/identity /impersonate:false 2>&1) -Level 2 Out-Log -Data (& $Script:appcmd set apppool MOData /processModel.identityType:SpecificUser 2>&1) -Level 2 Out-Log -Data (& $Script:appcmd set apppool MOData /processModel.userName:$($Credential.UserName) 2>&1) -Level 2 Out-Log -Data (& $Script:appcmd set apppool MOData /processModel.password:$(ConvertFrom-SecureToPlain -SecurePassword $Credential.Password) 2>&1) -Level 2 Out-Log -Data (& $Script:appcmd start apppool /apppool.name:MOData 2>&1) -Level 2 Out-Log -Data "Account '$($Credential.UserName)' added to IIS." -Level 1 $encodedAuthString = Convert-CredentialToHeaderAuthorizationString -Credential $Credential Out-Log -Data 'Use this string in the REST API request header:' -Level 0 Out-Log -Data " Authorization: Basic $encodedAuthString" -Level 0 -ForegroundColor Gray } Function Test-RESTAPI { Param ( [Parameter(Mandatory=$true)] [AllowNull()] $Credential ) if ($Credential -eq $null) { return } $encodedAuthString = Convert-CredentialToHeaderAuthorizationString -Credential $Credential $headers = @{ 'Authorization' = "Basic $encodedAuthString"; 'Content-Type' = 'application/json'; 'Accept' = 'application/json,application/xml'; } $url = "http://localhost:7000/MODataSvc/Microsoft.Management.OData.svc/QLGCFCAdapter?" Out-Log -Data "Testing REST API with $url" -ForegroundColor Cyan -Level 1 try { $response = (Invoke-RestMethod -Method Get -Uri $url -Headers $headers -Credential $Credential).value $response = ($response | Out-String).Trim() Out-Log -Data "`n$response`n" -Level 1 } 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 MRVLFC 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 MRVLFC 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) { Import-Module $moduleName -Force } } 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 } } Function Install-FCPowerKit { Param ( $response0, $response1, $response2 ) # Encapulate installer logic here #-------------------------------------------------------------------------------------------------- # Globals $Script:CLSID = '{A31B8A5E-8B6A-421C-8BB1-F85C9C34E659}' $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-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 # MIIqIAYJKoZIhvcNAQcCoIIqETCCKg0CAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCAe/46IPIpg8zyP # kdU5/q53F2pZT5KdAnq6/5kbRyJsXaCCDoswggawMIIEmKADAgECAhAIrUCyYNKc # 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/pQd52MbOoZWeE4wggfTMIIFu6ADAgECAhAPk5/xw2E4WPvGz6i0rW23MA0G # CSqGSIb3DQEBCwUAMGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwg # SW5jLjFBMD8GA1UEAxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcg # UlNBNDA5NiBTSEEzODQgMjAyMSBDQTEwHhcNMjEwODA2MDAwMDAwWhcNMjMwODA4 # MjM1OTU5WjCBtjELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFDAS # BgNVBAcTC1NhbnRhIENsYXJhMSQwIgYDVQQKExtNYXJ2ZWxsIFNlbWljb25kdWN0 # b3IsIEluYy4xCzAJBgNVBAsTAklUMSQwIgYDVQQDExtNYXJ2ZWxsIFNlbWljb25k # dWN0b3IsIEluYy4xIzAhBgkqhkiG9w0BCQEWFHJvZ2VybGl1QG1hcnZlbGwuY29t # MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAr4bpumsFGhFqGG7Bgas7 # ZEuy0kaczWPLM50MUrBRRxgqpmrBYMUPhsmXu7PdttPwGlcp7G3Q50kAPEra4nce # LK+vc8GYD96BttuVAxJiBsfq5Abe1iR5+1ZtKrequpNCDuihZpT6KhBW8nvFj4IY # +9ei2whK4xke2+DtisyHa3e/8hIopBvG0VoZlDrDjxYtR+bFbGaCRCJglkC8QcNI # x4TBWiuq2h4MZ08kfqeozCRKz8Ov7QB9si8OqrPX4ttPE6tFw9q/P1jFRFQ4hVJY # PfuMgv8OHdesPFM9N27VUl3cZAvD8lQDqBDrY4W9TW4NEq75ChCi8JbC9OaK+Nfp # ssU0o0kGpKPJXcmNkpXULLClfyD6MxciT4QBfxx0p/KSOD5D2bsr5T8tVOtARVl1 # bV3bxfxJXnaZ29ELwUq4DSa38j48MsnFCckv60/MGV6P4tAi0lolX8Pvtgwtov7K # x3KJemAql5VzKsR99prWLTNqqEbXBOflk684uad27hy6gjaQ3taR1szFjxy0c1uO # YvuT4OJgMEzKMFyL1AG0dshOi1qnMdeSH5ncHota/iGpIelRZxMP7juCP6HKVc4g # bXsfrbfHh6NlZTaVLMFxLVKgO6uTgCM2kHDssRX20BV5gAPslq0XJrE37QWrO+zH # E9JOKT5wmrA/PfTxx3T+9FUCAwEAAaOCAicwggIjMB8GA1UdIwQYMBaAFGg34Ou2 # O/hfEYb7/mF7CIhl9E5CMB0GA1UdDgQWBBS+sjWymf3OAbJHv2KMcCcJvXPlVDAf # BgNVHREEGDAWgRRyb2dlcmxpdUBtYXJ2ZWxsLmNvbTAOBgNVHQ8BAf8EBAMCB4Aw # EwYDVR0lBAwwCgYIKwYBBQUHAwMwgbUGA1UdHwSBrTCBqjBToFGgT4ZNaHR0cDov # L2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZEc0Q29kZVNpZ25pbmdS # U0E0MDk2U0hBMzg0MjAyMUNBMS5jcmwwU6BRoE+GTWh0dHA6Ly9jcmw0LmRpZ2lj # ZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNENvZGVTaWduaW5nUlNBNDA5NlNIQTM4 # NDIwMjFDQTEuY3JsMD4GA1UdIAQ3MDUwMwYGZ4EMAQQBMCkwJwYIKwYBBQUHAgEW # G2h0dHA6Ly93d3cuZGlnaWNlcnQuY29tL0NQUzCBlAYIKwYBBQUHAQEEgYcwgYQw # JAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBcBggrBgEFBQcw # AoZQaHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZEc0 # Q29kZVNpZ25pbmdSU0E0MDk2U0hBMzg0MjAyMUNBMS5jcnQwDAYDVR0TAQH/BAIw # ADANBgkqhkiG9w0BAQsFAAOCAgEAbZWAAFjUMBcc2V8y09EaiEk9JrKZzaf3gDNk # +jWkGPh7XwGiFFpo8dDRUvw2c40/+I7IWaUrfz9HyCZcrqfLAMguvAWq6+8DrEBC # 4uuJL18NuGu7y4yEdY4sKmDUb8U6rdkmTyI4+UhxAsIw/Eug3HnW4CYOUdWlDWcQ # StEIGI3zsbLzX59Czylvkmpb+rGJm2Cnu5n8cWuPe7axqd5dI//llr4/9OdSwau6 # vFwZZ6BqKpBtMrMiihxLCj8RQb9uPOHDa5US5MOCTbl9nXmy7KxmH9oRGOS5ZUYc # z+scn8KY0hyEsvSjNsRnUkBhDqfYCm9ou9zp4Bbr18CmYZ2TxKLmuWsXiT8lp+VA # QF+skDjvY8bfMUdQDU4aVQXddcDKR/5BUJ4ksbldVVqlqFNn34Ge5D05YMx3S654 # U27V/vdjdrITJiQT3x2l7ASh4RxpUtY59G4Bnx2HL/pHeCcWjsj6DdPBUF5UJpvc # PTkegweOzvRuYZdf7In4kw6xqqaexTferopUgM+qH2ojZ19Pn3H6RWGyou/8mWQK # CjxfQP2DPd4KD4Vlk9P5V25VHpH9z+H8hB0XxsTH4QRK6LbHhDw28D33Q9KsDCCd # toaTA2ohZDNKls6dB6KF9bEDxtkZlIAh6iGxvcY1Bs/lH8QPIHxOVCWzyhlPeqA9 # QwxDRcgxghrrMIIa5wIBATB9MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdp # Q2VydCwgSW5jLjFBMD8GA1UEAxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNp # Z25pbmcgUlNBNDA5NiBTSEEzODQgMjAyMSBDQTECEA+Tn/HDYThY+8bPqLStbbcw # DQYJYIZIAWUDBAIBBQCggeQwGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYK # KwYBBAGCNwIBCzEOMAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEICii5OH+ # Bd2pu349GzjnUFDajYdYUHXfJi3sNViGEEa8MHgGCisGAQQBgjcCAQwxajBooEyA # SgBNAGEAcgB2AGUAbABsACAAUQBMAG8AZwBpAGMAIABGAGkAYgByAGUAIABDAGgA # YQBuAG4AZQBsACAAUABvAHcAZQByAEsAaQB0oRiAFmh0dHA6Ly93d3cubWFydmVs # bC5jb20wDQYJKoZIhvcNAQEBBQAEggIAlDholXL8NXu+KulZ+pvLwXE5Y93JYZEP # +jv80ZGlTKPJjhMq1+j4iLwQ09UdFAbpcZuxfARw7RIE1TWrIf9sBvM59wLTdBs7 # 5Ekf+DqeUhm3odb3ejcul8NuDRCGNQiShFmkit0oM/gQQx/z1LCopWo1eOoG6BeW # 9Ltoe7UUDoBROP/VWdmlqyYh/Xst+bhEL2dVNyKr7s0pbuG/QozdNTnA3MkMnvfF # AaUGiTfP1gS1IuvA/qvp0+CE4tnupbE1wzGkLxq8JQiPjMMPVAzvA23bMWS0QWNK # iJZpAUyK1kJfi5It2oZDOh5k07AMr7qfzmkR0zyyEbbrc8/d4mYp5ddxM+WX4TKk # 5ogz8uyGnzIzCjycWk1splkoXQgqWxHrH9Sd/4e0Gy6zX+CmqC+2sGRdgvrQO3S0 # a1C7aAX0Y+AUDRfoMAcADw+qXvFXpTwMGNiDfumOtMAFps367gIDn3bxhQXlSm+b # iMkGZh28t6iH8Ouqcgrayey8Fw+2OIHAHJDlSVpLq++sh23LKFJ1miowVVrSMwg6 # NgYrXwbxlL+OE/EOdbyh9x4OG/bQ8/1RcxkX58IDpDcxmeCAc8Mr/xp020f01OMD # PDBDFQOf2Vu6KAH1IgVcY2Nrf4cnpMiqqsgGHHW1SF729oM6KrBwmW90Spe7KTKj # EC8O4Y8YC7ahghdYMIIXVAYKKwYBBAGCNwMDATGCF0QwghdABgkqhkiG9w0BBwKg # ghcxMIIXLQIBAzEPMA0GCWCGSAFlAwQCAQUAMGgGCyqGSIb3DQEJEAEEoFkEVzBV # AgEBBglghkgBhv1sBwEwITAJBgUrDgMCGgUABBTI6Vx467sttRPm9NXbF/9WOUO+ # jQIRAK32ditV6a8ReLzcutxspawYDzIwMjIwNzI3MDY1MzQxWqCCEzEwggbGMIIE # rqADAgECAhAKekqInsmZQpAGYzhNhpedMA0GCSqGSIb3DQEBCwUAMGMxCzAJBgNV # BAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjE7MDkGA1UEAxMyRGlnaUNl # cnQgVHJ1c3RlZCBHNCBSU0E0MDk2IFNIQTI1NiBUaW1lU3RhbXBpbmcgQ0EwHhcN # MjIwMzI5MDAwMDAwWhcNMzMwMzE0MjM1OTU5WjBMMQswCQYDVQQGEwJVUzEXMBUG # A1UEChMORGlnaUNlcnQsIEluYy4xJDAiBgNVBAMTG0RpZ2lDZXJ0IFRpbWVzdGFt # cCAyMDIyIC0gMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALkqliOm # XLxf1knwFYIY9DPuzFxs4+AlLtIx5DxArvurxON4XX5cNur1JY1Do4HrOGP5PIhp # 3jzSMFENMQe6Rm7po0tI6IlBfw2y1vmE8Zg+C78KhBJxbKFiJgHTzsNs/aw7ftwq # HKm9MMYW2Nq867Lxg9GfzQnFuUFqRUIjQVr4YNNlLD5+Xr2Wp/D8sfT0KM9CeR87 # x5MHaGjlRDRSXw9Q3tRZLER0wDJHGVvimC6P0Mo//8ZnzzyTlU6E6XYYmJkRFMUr # DKAz200kheiClOEvA+5/hQLJhuHVGBS3BEXz4Di9or16cZjsFef9LuzSmwCKrB2N # O4Bo/tBZmCbO4O2ufyguwp7gC0vICNEyu4P6IzzZ/9KMu/dDI9/nw1oFYn5wLOUr # sj1j6siugSBrQ4nIfl+wGt0ZvZ90QQqvuY4J03ShL7BUdsGQT5TshmH/2xEvkgMw # zjC3iw9dRLNDHSNQzZHXL537/M2xwafEDsTvQD4ZOgLUMalpoEn5deGb6GjkagyP # 6+SxIXuGZ1h+fx/oK+QUshbWgaHK2jCQa+5vdcCwNiayCDv/vb5/bBMY38ZtpHlJ # rYt/YYcFaPfUcONCleieu5tLsuK2QT3nr6caKMmtYbCgQRgZTu1Hm2GV7T4LYVrq # PnqYklHNP8lE54CLKUJy93my3YTqJ+7+fXprAgMBAAGjggGLMIIBhzAOBgNVHQ8B # Af8EBAMCB4AwDAYDVR0TAQH/BAIwADAWBgNVHSUBAf8EDDAKBggrBgEFBQcDCDAg # BgNVHSAEGTAXMAgGBmeBDAEEAjALBglghkgBhv1sBwEwHwYDVR0jBBgwFoAUuhbZ # bU2FL3MpdpovdYxqII+eyG8wHQYDVR0OBBYEFI1kt4kh/lZYRIRhp+pvHDaP3a8N # MFoGA1UdHwRTMFEwT6BNoEuGSWh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdp # Q2VydFRydXN0ZWRHNFJTQTQwOTZTSEEyNTZUaW1lU3RhbXBpbmdDQS5jcmwwgZAG # CCsGAQUFBwEBBIGDMIGAMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2Vy # dC5jb20wWAYIKwYBBQUHMAKGTGh0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9E # aWdpQ2VydFRydXN0ZWRHNFJTQTQwOTZTSEEyNTZUaW1lU3RhbXBpbmdDQS5jcnQw # DQYJKoZIhvcNAQELBQADggIBAA0tI3Sm0fX46kuZPwHk9gzkrxad2bOMl4IpnENv # AS2rOLVwEb+EGYs/XeWGT76TOt4qOVo5TtiEWaW8G5iq6Gzv0UhpGThbz4k5HXBw # 2U7fIyJs1d/2WcuhwupMdsqh3KErlribVakaa33R9QIJT4LWpXOIxJiA3+5Jlbez # zMWn7g7h7x44ip/vEckxSli23zh8y/pc9+RTv24KfH7X3pjVKWWJD6KcwGX0ASJl # x+pedKZbNZJQfPQXpodkTz5GiRZjIGvL8nvQNeNKcEiptucdYL0EIhUlcAZyqUQ7 # aUcR0+7px6A+TxC5MDbk86ppCaiLfmSiZZQR+24y8fW7OK3NwJMR1TJ4Sks3Kkzz # XNy2hcC7cDBVeNaY/lRtf3GpSBp43UZ3Lht6wDOK+EoojBKoc88t+dMj8p4Z4A2U # KKDr2xpRoJWCjihrpM6ddt6pc6pIallDrl/q+A8GQp3fBmiW/iqgdFtjZt5rLLh4 # qk1wbfAs8QcVfjW05rUMopml1xVrNQ6F1uAszOAMJLh8UgsemXzvyMjFjFhpr6s9 # 4c/MfRWuFL+Kcd/Kl7HYR+ocheBFThIcFClYzG/Tf8u+wQ5KbyCcrtlzMlkI5y2S # oRoR/jKYpl0rl+CL05zMbbUNrkdjOEcXW28T2moQbh9Jt0RbtAgKh1pZBHYRoad3 # AhMcMIIGrjCCBJagAwIBAgIQBzY3tyRUfNhHrP0oZipeWzANBgkqhkiG9w0BAQsF # ADBiMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQL # ExB3d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJv # b3QgRzQwHhcNMjIwMzIzMDAwMDAwWhcNMzcwMzIyMjM1OTU5WjBjMQswCQYDVQQG # EwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xOzA5BgNVBAMTMkRpZ2lDZXJ0 # IFRydXN0ZWQgRzQgUlNBNDA5NiBTSEEyNTYgVGltZVN0YW1waW5nIENBMIICIjAN # BgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxoY1BkmzwT1ySVFVxyUDxPKRN6mX # UaHW0oPRnkyibaCwzIP5WvYRoUQVQl+kiPNo+n3znIkLf50fng8zH1ATCyZzlm34 # V6gCff1DtITaEfFzsbPuK4CEiiIY3+vaPcQXf6sZKz5C3GeO6lE98NZW1OcoLevT # sbV15x8GZY2UKdPZ7Gnf2ZCHRgB720RBidx8ald68Dd5n12sy+iEZLRS8nZH92GD # Gd1ftFQLIWhuNyG7QKxfst5Kfc71ORJn7w6lY2zkpsUdzTYNXNXmG6jBZHRAp8By # xbpOH7G1WE15/tePc5OsLDnipUjW8LAxE6lXKZYnLvWHpo9OdhVVJnCYJn+gGkcg # Q+NDY4B7dW4nJZCYOjgRs/b2nuY7W+yB3iIU2YIqx5K/oN7jPqJz+ucfWmyU8lKV # EStYdEAoq3NDzt9KoRxrOMUp88qqlnNCaJ+2RrOdOqPVA+C/8KI8ykLcGEh/FDTP # 0kyr75s9/g64ZCr6dSgkQe1CvwWcZklSUPRR8zZJTYsg0ixXNXkrqPNFYLwjjVj3 # 3GHek/45wPmyMKVM1+mYSlg+0wOI/rOP015LdhJRk8mMDDtbiiKowSYI+RQQEgN9 # XyO7ZONj4KbhPvbCdLI/Hgl27KtdRnXiYKNYCQEoAA6EVO7O6V3IXjASvUaetdN2 # udIOa5kM0jO0zbECAwEAAaOCAV0wggFZMBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYD # VR0OBBYEFLoW2W1NhS9zKXaaL3WMaiCPnshvMB8GA1UdIwQYMBaAFOzX44LScV1k # TN8uZz/nupiuHA9PMA4GA1UdDwEB/wQEAwIBhjATBgNVHSUEDDAKBggrBgEFBQcD # CDB3BggrBgEFBQcBAQRrMGkwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2lj # ZXJ0LmNvbTBBBggrBgEFBQcwAoY1aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29t # L0RpZ2lDZXJ0VHJ1c3RlZFJvb3RHNC5jcnQwQwYDVR0fBDwwOjA4oDagNIYyaHR0 # cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZFJvb3RHNC5jcmww # IAYDVR0gBBkwFzAIBgZngQwBBAIwCwYJYIZIAYb9bAcBMA0GCSqGSIb3DQEBCwUA # A4ICAQB9WY7Ak7ZvmKlEIgF+ZtbYIULhsBguEE0TzzBTzr8Y+8dQXeJLKftwig2q # KWn8acHPHQfpPmDI2AvlXFvXbYf6hCAlNDFnzbYSlm/EUExiHQwIgqgWvalWzxVz # jQEiJc6VaT9Hd/tydBTX/6tPiix6q4XNQ1/tYLaqT5Fmniye4Iqs5f2MvGQmh2yS # vZ180HAKfO+ovHVPulr3qRCyXen/KFSJ8NWKcXZl2szwcqMj+sAngkSumScbqyQe # JsG33irr9p6xeZmBo1aGqwpFyd/EjaDnmPv7pp1yr8THwcFqcdnGE4AJxLafzYeH # JLtPo0m5d2aR8XKc6UsCUqc3fpNTrDsdCEkPlM05et3/JWOZJyw9P2un8WbDQc1P # tkCbISFA0LcTJM3cHXg65J6t5TRxktcma+Q4c6umAU+9Pzt4rUyt+8SVe+0KXzM5 # h0F4ejjpnOHdI/0dKNPH+ejxmF/7K9h+8kaddSweJywm228Vex4Ziza4k9Tm8heZ # Wcpw8De/mADfIBZPJ/tgZxahZrrdVcA6KYawmKAr7ZVBtzrVFZgxtGIJDwq9gdkT # /r+k0fNX2bwE+oLeMt8EifAAzV3C+dAjfwAL5HYCJtnwZXZCpimHCUcr5n8apIUP # /JiW9lVUKx+A+sDyDivl1vupL0QVSucTDh3bNzgaoSv27dZ8/DCCBbEwggSZoAMC # AQICEAEkCvseOAuKFvFLcZ3008AwDQYJKoZIhvcNAQEMBQAwZTELMAkGA1UEBhMC # VVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0 # LmNvbTEkMCIGA1UEAxMbRGlnaUNlcnQgQXNzdXJlZCBJRCBSb290IENBMB4XDTIy # MDYwOTAwMDAwMFoXDTMxMTEwOTIzNTk1OVowYjELMAkGA1UEBhMCVVMxFTATBgNV # BAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEhMB8G # A1UEAxMYRGlnaUNlcnQgVHJ1c3RlZCBSb290IEc0MIICIjANBgkqhkiG9w0BAQEF # AAOCAg8AMIICCgKCAgEAv+aQc2jeu+RdSjwwIjBpM+zCpyUuySE98orYWcLhKac9 # WKt2ms2uexuEDcQwH/MbpDgW61bGl20dq7J58soR0uRf1gU8Ug9SH8aeFaV+vp+p # VxZZVXKvaJNwwrK6dZlqczKU0RBEEC7fgvMHhOZ0O21x4i0MG+4g1ckgHWMpLc7s # Xk7Ik/ghYZs06wXGXuxbGrzryc/NrDRAX7F6Zu53yEioZldXn1RYjgwrt0+nMNlW # 7sp7XeOtyU9e5TXnMcvak17cjo+A2raRmECQecN4x7axxLVqGDgDEI3Y1DekLgV9 # iPWCPhCRcKtVgkEy19sEcypukQF8IUzUvK4bA3VdeGbZOjFEmjNAvwjXWkmkwuap # oGfdpCe8oU85tRFYF/ckXEaPZPfBaYh2mHY9WV1CdoeJl2l6SPDgohIbZpp0yt5L # HucOY67m1O+SkjqePdwA5EUlibaaRBkrfsCUtNJhbesz2cXfSwQAzH0clcOP9yGy # shG3u3/y1YxwLEFgqrFjGESVGnZifvaAsPvoZKYz0YkH4b235kOkGLimdwHhD5QM # IR2yVCkliWzlDlJRR3S+Jqy2QXXeeqxfjT/JvNNBERJb5RBQ6zHFynIWIgnffEx1 # P2PsIV/EIFFrb7GrhotPwtZFX50g/KEexcCPorF+CiaZ9eRpL5gdLfXZqbId5RsC # AwEAAaOCAV4wggFaMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFOzX44LScV1k # TN8uZz/nupiuHA9PMB8GA1UdIwQYMBaAFEXroq/0ksuCMS1Ri6enIZ3zbcgPMA4G # A1UdDwEB/wQEAwIBhjATBgNVHSUEDDAKBggrBgEFBQcDCDB5BggrBgEFBQcBAQRt # MGswJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBDBggrBgEF # BQcwAoY3aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJl # ZElEUm9vdENBLmNydDBFBgNVHR8EPjA8MDqgOKA2hjRodHRwOi8vY3JsMy5kaWdp # Y2VydC5jb20vRGlnaUNlcnRBc3N1cmVkSURSb290Q0EuY3JsMCAGA1UdIAQZMBcw # CAYGZ4EMAQQCMAsGCWCGSAGG/WwHATANBgkqhkiG9w0BAQwFAAOCAQEAmhYCpQHv # gfsNtFiyeK2oIxnZczfaYJ5R18v4L0C5ox98QE4zPpA854kBdYXoYnsdVuBxut5e # xje8eVxiAE34SXpRTQYy88XSAConIOqJLhU54Cw++HV8LIJBYTUPI9DtNZXSiJUp # Q8vgplgQfFOOn0XJIDcUwO0Zun53OdJUlsemEd80M/Z1UkJLHJ2NltWVbEcSFCRf # JkH6Gka93rDlkUcDrBgIy8vbZol/K5xlv743Tr4t851Kw8zMR17IlZWt0cu7KgYg # +T9y6jbrRXKSeil7FAM8+03WSHF6EBGKCHTNbBsEXNKKlQN2UVBT1i73SkbDrhAs # cUywh7YnN0RgRDGCA3YwggNyAgEBMHcwYzELMAkGA1UEBhMCVVMxFzAVBgNVBAoT # DkRpZ2lDZXJ0LCBJbmMuMTswOQYDVQQDEzJEaWdpQ2VydCBUcnVzdGVkIEc0IFJT # QTQwOTYgU0hBMjU2IFRpbWVTdGFtcGluZyBDQQIQCnpKiJ7JmUKQBmM4TYaXnTAN # BglghkgBZQMEAgEFAKCB0TAaBgkqhkiG9w0BCQMxDQYLKoZIhvcNAQkQAQQwHAYJ # KoZIhvcNAQkFMQ8XDTIyMDcyNzA2NTM0MVowKwYLKoZIhvcNAQkQAgwxHDAaMBgw # FgQUhQjzhlFcs9MHfba0t8B/G0peQd4wLwYJKoZIhvcNAQkEMSIEICEMkb7oHyEw # AlscZvL9vHHiBleqHQXBGUQdaDDnopnnMDcGCyqGSIb3DQEJEAIvMSgwJjAkMCIE # IJ2mkBXDScbBiXhFujWCrXDIj6QpO9tqvpwr0lOSeeY7MA0GCSqGSIb3DQEBAQUA # BIICAA+I5Dc+S778ETXVSQEuhkRVCyeIQueD0k87DQfoS4n3qu9+goIL7RQ8BAay # wmIX94aUQ5Bpt04ZalmwJ+txTPszF2WbeJt3VOmaMRgWhnTCNzly6RscoUSsglVL # 58eClDmoY61LRKerlQYig1waOh5OcDUht6ibSbQr9gHQCSEkZhRvBQ8JZx8y8Xmn # rtvjROJD5RgEryptlWHjDMI1DyenRltML+K5yhruBA7q9gvTkxHZCsriT/AgTmc9 # Q+uf2HhJz7yrq+7M1baEx03ryE2V/ozzjFcQomkS22Zee4hz0kGd3F25Bk6DEO5M # 1IY5y5MABx4bgkuSXuU87LPzZuVGLlPQSXLqW1X7EZDTF4MrdLdLcjw6YXDig1IN # 47rx9neJ/vzD7Dn9fIJa/Bw4BOkdykN+clHq3/bNdetmOdQBZ+2g2hsisNoaY5Sr # PVbROuOMFGwxRC9yZ7ZnyP/07+tsz3hP8Az9wuEB/IOPOaVdZcSn/CUtAuyS1qqA # g8zc8fvpXoQYJyxWyIK4Xyj7A/AgPos3PwMHkzPwZwcB/k9BWpj39UqacmYTSZsx # 7IKUtay9LW/BgMT7wOYkreQYYKvN8ATmHd27rxxlMKBhXt6ofyNtt57d3KA4WwC0 # b+NoPJDVd9yrdp+ECSfvh2CWZtjKdSOHsK3I8oGAI05A23oh # SIG # End signature block |