AzSHCI.ARCInstaller.psm1
<#############################################################
# # # Copyright (C) Microsoft Corporation. All rights reserved. # # # #############################################################> Import-Module $PSScriptRoot\Classes\reporting.psm1 -Force -DisableNameChecking -Global function Check-NodeArcRegistrationStateScriptBlock { if(Test-Path -Path "C:\Program Files\AzureConnectedMachineAgent\azcmagent.exe") { $arcAgentStatus = Invoke-Expression -Command "& 'C:\Program Files\AzureConnectedMachineAgent\azcmagent.exe' show -j" # Parsing the status received from Arc agent $arcAgentStatusParsed = $arcAgentStatus | ConvertFrom-Json # Throw an error if the node is Arc enabled to a different resource group or subscription id # Agent can be is "Connected" or disconnected state. If the resource name property on the agent is empty, that means, it is cleanly disconnected , and just the exe exists # If the resourceName exists and agent is in "Disconnected" state, indicates agent has temporary connectivity issues to the cloud if(-not ([string]::IsNullOrEmpty($arcAgentStatusParsed.resourceName)) -or -not ([string]::IsNullOrEmpty($arcAgentStatusParsed.subscriptionId)) -or -not ([string]::IsNullOrEmpty($arcAgentStatusParsed.resourceGroup)) ) { $differentResourceExceptionMessage = "Node is already ARC Enabled and connected to Subscription Id: {0}, Resource Group: {1}" -f $arcAgentStatusParsed.subscriptionId, $arcAgentStatusParsed.resourceGroup Log-info -Message "$differentResourceExceptionMessage" -Type Error -ConsoleOut return [ErrorDetail]::NodeAlreadyArcEnabled } return [ErrorDetail]::Success } } function Register-ResourceProviderIfRequired { param( [string] $ProviderNamespace ) $rpState = Get-AzResourceProvider -ProviderNamespace $ProviderNamespace $notRegisteredResourcesForRP = ($rpState.Where({$_.RegistrationState -ne "Registered"}) | Measure-Object ).Count if ($notRegisteredResourcesForRP -eq 0 ) { Log-Info -Message "$ProviderNamespace RP already registered, skipping registration" -ConsoleOut } else { try { Register-AzResourceProvider -ProviderNamespace $ProviderNamespace | Out-Null Log-Info -Message "registered Resource Provider: $ProviderNamespace " -ConsoleOut } catch { Log-Info -Message "Exception occured while registering $ProviderNamespace RP, $_" -ConsoleOut throw } } } function Show-BootstrapStatus { param( [System.Object] $Response ) Log-Info -Message "$($Response.Name) : $($Response.Status)" -ConsoleOut foreach($response in $Response.DetailedResponse) { Log-Info -Message " $($response.Name) : $($response.Status)" -ConsoleOut } } ############################################################################################################ # Change the proxy settings for the environment ############################################################################################################ function Is-IpRange { param ( [string]$IP ) $ipRangeRegex = "^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)\.?){1,3}(\.\*){1,3}$" return [regex]::IsMatch($IP, $ipRangeRegex) } function Convert-ToCidrAddress { param ( [string]$ipRange ) if (-not (Is-IpRange $ipRange)) { throw "IP range format exception: $ipRange" } $ipParts = $ipRange.Split('.') $cidrAddress = "" $maskLength = 0 for ($i = 0; $i -lt 4; $i++) { if ($i -ge $ipParts.Length -or $ipParts[$i] -eq "*") { $cidrAddress += "0" } elseif ($ipParts[$i] -match '^\d+$') { $cidrAddress += $ipParts[$i] $maskLength++ } else { throw "IP range format exception: $ipRange" } if ($i -lt 3) { $cidrAddress += "." } } $cidrAddress = "$cidrAddress/$($maskLength * 8)" return $cidrAddress } function Convert-BypassStringForEnv { param ( [string] $bypassString ) $formattedBypassList = "" $svcEntry = ".svc" $bypassList = $bypassString.Split(';') foreach ($entry in $bypassList) { $entry = $entry.Trim() if ($entry.StartsWith("*")) { $formattedBypassList += $entry.Substring(1) } elseif (Is-IpRange $entry) { $formattedBypassList += (Convert-ToCidrAddress -ipRange $entry) } else { $formattedBypassList += $entry } $formattedBypassList += ',' } if ($formattedBypassList -notcontains $svcEntry) { $formattedBypassList += $svcEntry } $formattedBypassList = $formattedBypassList.Trim(',') Log-Info -Message "Formatted list: $($formattedBypassList)" -ConsoleOut return [string] $formattedBypassList } function Configure-EnvironmentProxy { param( [string] $NetworkProxy, [string] $Bypass ) $urlRegex = "http://([^:]+):(\d+)" if (-not ($NetworkProxy -match $urlRegex)) { throw "Invalid proxy URL. Please provide a valid URL in the format http://<proxy>:<port>" } if ([string]::IsNullOrEmpty($Bypass)) { throw "Bypass list cannot be empty. Please provide a valid bypass list in the format URL1;URL2;URL3 etc. The list needs to be separated by semicolon" } if ($Bypass -like "*,*") { throw "Invalid bypass list. Please provide a valid bypass list in the format URL1;URL2;URL3 etc. The list needs to be separated by semicolon" } if (-not (Get-Module -ListAvailable -Name WinInetProxy)) { Install-Module -Name WinInetProxy -Force -AllowClobber -Scope AllUsers -ErrorAction SilentlyContinue } Set-WinInetProxy -ProxySettingsPerUser 0 -ProxyServer "http=$($NetworkProxy);https=$($NetworkProxy)" -ProxyBypass $Bypass Set-winhttpproxy -proxyserver "http=$($NetworkProxy);https=$($NetworkProxy)" -BypassList $Bypass [Environment]::SetEnvironmentVariable("HTTP_PROXY", $NetworkProxy, "Machine") [Environment]::SetEnvironmentVariable("HTTPS_PROXY", $NetworkProxy, "Machine") if (-not [string]::IsNullOrEmpty($Bypass)) { [Environment]::SetEnvironmentVariable("NO_PROXY", (Convert-BypassStringForEnv -bypassString $Bypass), "Machine") } Log-Info -Message "Successfully configured the host to route traffic via Customer's Network Proxy" -ConsoleOut # Read the environment variables to verify the changes $env:HTTP_PROXY = [System.Environment]::GetEnvironmentVariable("HTTP_PROXY", "Machine") $env:HTTPS_PROXY = [System.Environment]::GetEnvironmentVariable("HTTPS_PROXY", "Machine") $env:NO_PROXY = [System.Environment]::GetEnvironmentVariable("NO_PROXY", "Machine") $netshoutput = netsh winhttp show proxy | Out-String Log-Info -Message "WinHTTP: $($netshoutput)" -ConsoleOut Log-Info -Message "HTTP_PROXY: $($env:HTTP_PROXY), HTTPS_PROXY: $($env:HTTPS_PROXY), NO_PROXY: $($env:NO_PROXY) " -ConsoleOut } ############################################################################################################ ############################################################################################################ # Configure to use Arc Proxy as proxy for all https host traffic and ARB & AKS traffic ############################################################################################################ function Get-ConfiguredBypassList { $proxySetting = netsh winhttp show proxy $proxyBypassString = "" foreach ($setting in $proxySetting) { if ($setting -like "*Bypass*"){ $proxyBypassString = $setting break; } } $bypassList = $proxyBypassString -split ":", 2 | Select-Object -Last 1 # Trim any leading or trailing whitespace $bypassList = $bypassList.Trim() return [string]$bypassList } function Configure-PortForwardToArcProxy { param( [string] $ArcListenerPort ) $port_proxy_forward_port = "59031" try { netsh interface portproxy add v4tov4 listenaddress=* listenport=59031 connectaddress=localhost connectport=$ArcListenerPort New-NetFirewallRule -DisplayName "Arc Proxy Listener" -Direction Inbound -Profile Any -Action Allow -LocalPort $port_proxy_forward_port -Protocol TCP $port_forward_output = netsh interface portproxy show all | Out-String Log-Info -Message "Port forward rule set to `n $($port_forward_output)" -ConsoleOut $firewallOutput = (Get-NetFirewallRule -DisplayName "Arc Proxy Listener" -ErrorAction Ignore) | Out-String Log-Info -Message "Firewall rule set to `n $($firewallOutput)" -ConsoleOut # This is a temporary workaround and will be removed once the crash root cause is identified. # Till Arc proxy is able to support configuring the listener, we are relying on port forwarding rule to # forward traffic to Arc proxy. We have seen crashes both in Arc proxy and iphlpsvc, following modifications # in recovery options ensure that these services are restarted more frequently to avoid any downtime. Log-Info -Message "Change the recovery options for iphlpsvc to reset failure counter every few mins" -ConsoleOut sc.exe failure iphlpsvc reset=310 actions= restart/30000/restart/30000/restart/30000/restart/30000/restart/30000/restart/30000/restart/30000/restart/30000/restart/30000/restart/30000/ Log-Info "$(sc.exe qfailure iphlpsvc | Out-String)" -ConsoleOut } catch { Remove-NetFirewallRule -DisplayName "Arc Proxy Listener" -ErrorAction Ignore Log-Info -Message "Failed to configure port forwarding to Arc Proxy" -ConsoleOut throw "Failed to configure port forwarding to Arc Proxy" } } function Configure-VMOutboundToUseArcProxy { param( [string] $ArcListenerPort ) # Remove the firewall rule if it exists Remove-NetFirewallRule -DisplayName "Arc Proxy Listener" -ErrorAction Ignore $supportedVersion = [System.Version]::Parse("1.45") $connectedMachineAgentVersion = & "$env:ProgramW6432\AzureConnectedMachineAgent\azcmagent.exe" --version $arcproxyListenerConfigSupported = [regex]::Match($connectedMachineAgentVersion, '\d+(\.\d+)+').Value -ge $supportedVersion if ($arcproxyListenerConfigSupported) { # Configure the Arc proxy's listener and manage the firewall rule & "$env:ProgramW6432\AzureConnectedMachineAgent\azcmagent.exe" config set arcproxy.listenaddress 0.0.0.0 $listenerIPConfigured = & "$env:ProgramW6432\AzureConnectedMachineAgent\azcmagent.exe" config get arcproxy.listenaddress if ($listenerIPConfigured -eq "0.0.0.0") { Log-Info -Message "Successfully configured Arc Proxy's listener" -ConsoleOut New-NetFirewallRule -DisplayName "Arc Proxy Listener" -Direction Inbound -Profile Any -Action Allow -LocalPort $ArcListenerPort -Protocol TCP $firewallOutput = (Get-NetFirewallRule -DisplayName "Arc Proxy Listener" -ErrorAction Ignore) | Out-String Log-Info -Message "Firewall rule set to `n $($firewallOutput)" -ConsoleOut } else { Log-Info -Message "Failed to configure Arc Proxy's listener" -ConsoleOut throw "Failed to configure Arc Proxy's listener" } } else { Log-Info -Message "Arc Proxy version does not support configuring the listener. Use port forwarding to route traffic via Arc Proxy" -ConsoleOut Configure-PortForwardToArcProxy -ArcListenerPort $ArcListenerPort } } function Configure-HostOutboundToUseArcProxy { param( [string] $ArcURL, [string] $NetworkProxy, [string] $ProxyBypass ) $requiredBypasses = @("localhost","127.0.0.1") ## TBD - Might not need following URLs since we are configuring separate proxy for HTTP requests: # "*.tlu.dl.delivery.mp.microsoft.com", "ocsp.digicert.com", "crl3.digicert.com", "ctldl.windowsupdate.com" # Once we have Arc proxy that supports HTTP requests, we will not need to worry about separate HTTP setting # For environments with Proxy: # Read the bypass list first from parameter passed in. If that is null, read the system configured setting (to support backward compatibility). # If both of them are null, throw an error # For environment without Proxy: Ignore the Proxybypass and let it be empty $proxyServerNoProxy = $ProxyBypass if([string]::IsNullOrEmpty($proxyServerNoProxy) -and -not [string]::IsNullOrEmpty($NetworkProxy)) { $proxyServerNoProxy = Get-ConfiguredBypassList if ([string]::IsNullOrEmpty($proxyServerNoProxy)) { throw "Invalid bypass list.Please provide a valid bypass list in the format URL1;URL2;URL3 etc. The list needs to be separated by semicolon" } } # convert the comma-separated string into an array to make search easier below $bypasses = $proxyServerNoProxy.Split(';') foreach ($bypass in $requiredBypasses) { if ($bypasses -notcontains $bypass) { Trace-Execution "Adding $bypass to bypass list" $proxyServerNoProxy += ";$bypass" } } Log-Info -Message "Configuring bypass list as: $proxyServerNoProxy" -ConsoleOut $proxyServerNoProxy = $proxyServerNoProxy.Trim().Trim(";") # Set http proxy to customer's proxy as we don't want to route http traffic via Arc proxy & # Set https proxy to Arc proxy to route all https traffic via Arc proxy + Arc Gateway if (-not (Get-Module -ListAvailable -Name WinInetProxy)) { Install-Module -Name WinInetProxy -Force -AllowClobber -Scope AllUsers -ErrorAction SilentlyContinue } Set-WinInetProxy -ProxySettingsPerUser 0 -ProxyServer "http=$($NetworkProxy);https=$($ArcURL)" -ProxyBypass $proxyServerNoProxy Set-winhttpproxy -proxyserver "http=$($NetworkProxy);https=$($ArcURL)" -BypassList $proxyServerNoProxy # Similar to above: HTTP -> Customer's proxy & HTTPS -> Arc proxy # Do not set the environment variables to point to Arc proxy if no proxy is passed in. if (-not [string]::IsNullOrEmpty($NetworkProxy)) { # Setting this when no proxy was explicitly passed in causes ArcProxy to go in recursive connection to itself Log-Info -Message "Only set this when there was a network proxy passed in. " -ConsoleOut [Environment]::SetEnvironmentVariable("HTTP_PROXY", $NetworkProxy, "Machine") [Environment]::SetEnvironmentVariable("HTTPS_PROXY", $ArcURL, "Machine") # Workaround for the issue where ArcProxy's probe request (HTTP) ends up going to NetworkProxy and Arc proxy fails to start # when we configure Arc Proxy's listener to be on 0.0.0.0. This is a temporary workaround and can be removed once Arc Proxy brings in # the fix to handle this scenario. $env_no_proxy = (Convert-BypassStringForEnv -bypassString $proxyServerNoProxy) + ",0.0.0.0" [Environment]::SetEnvironmentVariable("NO_PROXY", $env_no_proxy, "Machine") # Restart the ArcProxy service to pick up the new environment variables Restart-Service ArcProxy } # Read the environment variables to verify the changes $env:HTTP_PROXY = [System.Environment]::GetEnvironmentVariable("HTTP_PROXY", "Machine") $env:HTTPS_PROXY = [System.Environment]::GetEnvironmentVariable("HTTPS_PROXY", "Machine") $env:NO_PROXY = [System.Environment]::GetEnvironmentVariable("NO_PROXY", "Machine") Log-Info -Message "Successfully configured the host to route traffic via the Arc Proxy" -ConsoleOut $netshoutput = netsh winhttp show proxy | Out-String Log-Info -Message "WinHTTP: $($netshoutput)" -ConsoleOut Log-Info -Message "HTTP_PROXY: $($env:HTTP_PROXY), HTTPS_PROXY: $($env:HTTPS_PROXY), NO_PROXY: $($env:NO_PROXY) " -ConsoleOut } function RouteAllTrafficViaArcProxy { param( [string] $NetworkProxy, [string] $ProxyBypass ) $arcConnectionType = & "$env:ProgramW6432\AzureConnectedMachineAgent\azcmagent.exe" config get connection.type if( $arcConnectionType -eq "gateway") { $urlRegex = "http://([^:]+):(\d+)" $arc_proxy_url = & "$env:ProgramW6432\AzureConnectedMachineAgent\azcmagent.exe" show "Using HTTPS Proxy" $arcproxy_listener_port = "" if ($arc_proxy_url -match $urlRegex) { $url = $matches[0] $arcproxy_listener_hostname = $matches[1] $arcproxy_listener_port = $matches[2] $arc_proxy_url = "http://$($arcproxy_listener_hostname):$($arcproxy_listener_port)" Log-Info -Message "Arc Proxy URL: $($arc_proxy_url), hostname:$($arcproxy_listener_hostname) Port: $($arcproxy_listener_port)" -ConsoleOut } Log-Info -Message "Configuring Host outbound Arc URL: $($arc_proxy_url), Customer proxy:$($NetworkProxy)" -ConsoleOut # Configure all host to route traffic via the Arc Proxy Configure-HostOutboundToUseArcProxy -ArcURL $arc_proxy_url -NetworkProxy $NetworkProxy -ProxyBypass $ProxyBypass Log-Info "Configuring port forwarding on $($arcproxy_listener_port)" -ConsoleOut Configure-VMOutboundToUseArcProxy -ArcListenerPort $arcproxy_listener_port # This is a temporary workaround and can be removed once Arc proxy brings in bug fix on race condition. # Also this change should not affect any functionality. It is just configuring the recovery options for ArcProxy to reset failure counter every few mins sc.exe failure ArcProxy reset=110 actions= restart/10000/restart/10000/restart/10000/restart/10000/restart/10000/restart/10000/restart/10000/restart/10000/restart/10000/restart/10000/ Log-Info -Message "Change the recovery options for ArcProxy reset failure counter every few mins" -ConsoleOut Log-Info "$(sc.exe qfailure ArcProxy | Out-String)" -ConsoleOut } } function ValidateCloudFqdn() { param( [string] $CloudFqdn ) try { if ([string]::IsNullOrEmpty($CloudFqdn)) { return $false } $Url = "armmanagement.$CloudFqdn" $response = Test-Connection -ComputerName $Url -Count 1 -ErrorAction Stop return $true } catch { return $false } } ############################################################################################################ function Invoke-AzStackHciArcInitialization { <# .SYNOPSIS Perform AzStackHci ArcIntegration Initialization .DESCRIPTION Initializes ARC integration on Azure Stack HCI node .EXAMPLE PS C:\> Connect-AzAccount -Tenant $tenantID -Subscription $subscriptionID -DeviceCode PS C:\> $nodeNames = [string[]]("host1","host2","host3","host4") PS C:\> Invoke-AzStackHciArcIntegrationValidation -SubscriptionID $subscriptionID -ArcResourceGroupName $resourceGroupName -NodeNames $nodeNames .PARAMETER SubscriptionID Specifies the Azure Subscription to create the resource. Is Mandatory Paratmer .PARAMETER ResourceGroup Specifies the resource group to which ARC resources should be projected. Is Mandatory Paratmer .PARAMETER TenantID Specifies the Azure TenantId.Required only if ARMAccessToken is used. .PARAMETER Cloud Specifies the Azure Environment. Valid values are AzureCloud, AzureChinaCloud, AzureUSGovernment, Azure.local. Required only if ARMAccessToken is used. .PARAMETER Region Specifies the Region to create the resource. Region is a Mandatory parameter. .PARAMETER ArmAccessToken Specifies the ARM access token. Specifying this along with AccountId will avoid Azure interactive logon. If not specified, Azure Context is expected to be setup. .PARAMETER AccountID Specifies the Account Id. Specifying this along with ArmAccessToken will avoid Azure interactive logon. Required only if ARMAccessToken is used. .PARAMETER SpnCredential Specifies the Service Principal Credential. Required only if ARMAccessToken is not used. .PARAMETER Tag Specifies the resource tags for the resource in Azure in the form of key-value pairs in a hash table. For example: @{key0="value0";key1=$null;key2="value2"} .PARAMETER EnvironmentCheckerVersion Specifies the Version of Environment Checker to be used. Default is latest version. .PARAMETER OutputPath Directory path for log and report output. .PARAMETER Proxy Specify proxy server. .PARAMETER ProxyBypass List of endpoints that bypass the proxy server. .PARAMETER ArcGatewayID Specify Arc Gateway resource Id. #> [CmdletBinding(DefaultParametersetName='AZContext')] param ( [Parameter(ParameterSetName='SPN', Mandatory = $true, HelpMessage = "Azure Subscription ID to project ARC resource ")] [Parameter(ParameterSetName='ARMToken', Mandatory = $true, HelpMessage = "Azure Subscription ID to project ARC resource ")] [string] $SubscriptionID, #TODO: should we do a validation of if the resource group is created or should we create the RG ? [Parameter(ParameterSetName='SPN', Mandatory = $true, HelpMessage = "Azure Resource group used for HCI ARC Integration")] [Parameter(ParameterSetName='ARMToken', Mandatory = $true, HelpMessage = "Azure Resource group used for HCI ARC Integration")] [ValidateScript({ if ($_ -match '^[a-zA-Z0-9_()\-\.]{1,89}[a-zA-Z0-9_()\-]$') { $true } else { throw "ResourceGroup is invalid. The requirements for resource group names are: 1.Between 1 and 90 characters long. 2.Alphanumeric characters, underscores, parentheses, hyphens and periods. 3.Cannot end with a period." } })] [string] $ResourceGroup, [Parameter(ParameterSetName='SPN', Mandatory = $true, HelpMessage = "Azure Tenant used for HCI ARC Integration")] [Parameter(ParameterSetName='ARMToken', Mandatory = $true, HelpMessage = "Azure Tenant used for HCI ARC Integration")] [string] $TenantID, # AzureCloud , AzureUSGovernment , AzureChinaCloud , Azure.local [Parameter(ParameterSetName='SPN', Mandatory = $true, HelpMessage = "Azure Cloud type used for HCI ARC Integration. Valid values are : AzureCloud , AzureUSGovernment , AzureChinaCloud, Azure.local")] [Parameter(ParameterSetName='ARMToken', Mandatory = $true, HelpMessage = "Azure Cloud type used for HCI ARC integration. Valid values are : AzureCloud , AzureUSGovernment , AzureChinaCloud, Azure.local")] [string] $Cloud, [Parameter(ParameterSetName='SPN', Mandatory = $true, HelpMessage = "Azure Region used for HCI ARC Integration")] [Parameter(ParameterSetName='ARMToken', Mandatory = $true, HelpMessage = "Azure Region used for HCI ARC Integration")] [string] $Region, [Parameter(ParameterSetName='ARMToken', Mandatory = $true, HelpMessage = "ARM Access Token used for HCI ARC Integration")] [string] $ArmAccessToken, [Parameter(ParameterSetName='ARMToken', Mandatory = $true, HelpMessage = "Account ID used for HCI ARC Integration")] [string] $AccountID, [Parameter(ParameterSetName='SPN', Mandatory = $true, HelpMessage = "SPN credential used for onboarding AR")] [System.Management.Automation.PSCredential] $SpnCredential, [Parameter(ParameterSetName='SPN', Mandatory=$false)] [Parameter(ParameterSetName='ARMToken', Mandatory = $false, HelpMessage = "Return PSObject result.")] [Parameter(Mandatory = $false)] [System.Collections.Hashtable] $Tag, [Parameter(ParameterSetName='SPN', Mandatory=$false)] [Parameter(ParameterSetName='ARMToken', Mandatory = $false, HelpMessage = "Return PSObject result.")] [Parameter(Mandatory = $false)] [System.Version]$EnvironmentCheckerVersion, [Parameter(ParameterSetName='SPN', Mandatory=$false)] [Parameter(ParameterSetName='ARMToken', Mandatory = $false, HelpMessage = "Directory path for log and report output")] [string]$OutputPath, [Parameter(ParameterSetName='SPN', Mandatory=$false, HelpMessage = "Specify proxy server.")] [Parameter(ParameterSetName='ARMToken', Mandatory=$false, HelpMessage = "Specify proxy server.")] [string] $Proxy, [Parameter(ParameterSetName='SPN', Mandatory=$false, HelpMessage = "Bypass proxy server.")] [Parameter(ParameterSetName='ARMToken', Mandatory=$false, HelpMessage = "Bypass proxy server.")] [string] $ProxyBypass, [Parameter(ParameterSetName='SPN', Mandatory=$false, HelpMessage = "Specify Gateway resource Id.")] [Parameter(ParameterSetName='ARMToken', Mandatory=$false, HelpMessage = "Specify Gateway resource Id.")] [string] $ArcGatewayID, [Parameter(ParameterSetName='SPN', Mandatory=$false, HelpMessage = "Local FQDN for Azure.local")] [Parameter(ParameterSetName='ARMToken', Mandatory=$false, HelpMessage = "Local FQDN for Azure.local")] [string] $CloudFqdn, [Parameter(Mandatory = $false)] [Switch] $Force, [Parameter(Mandatory = $false)] [Switch] $SkipExtensionInstall ) $tempConfigPath = "$env:TEMP\bootstrap.json" $cmdletFailed = $false try { $script:ErrorActionPreference = 'Stop' $ProgressPreference = 'SilentlyContinue' Set-AzStackHciOutputPath -Path $OutputPath if ($Cloud -eq "Azure.local") { if (-not (ValidateCloudFqdn -CloudFqdn $CloudFqdn -ErrorAction SilentlyContinue)) { Log-Info -Message "Invalid Configuration - A valid CloudFqdn value is required when the ‘Cloud’ parameter is set to ‘Azure.local’" -Type Error -ConsoleOut throw "Invalid Configuration - A valid CloudFqdn value is required when the ‘Cloud’ parameter is set to ‘Azure.local’" } } Log-Info -Message "Starting AzStackHci ArcIntegration Initialization" -ConsoleOut if (-not (Get-Command -Name Start-ArcBootstrap -ErrorAction SilentlyContinue)) { # Configure environment proxy settings only if both Proxy and Bypass are provided as parameters # otherwise assume it is older path and customer already configured the proxy settings before calling this cmdlet if (-not [string]::IsNullOrEmpty($Proxy) -and (-not [string]::IsNullOrEmpty($ProxyBypass))) { Log-Info -Message "Configuring Environment proxy settings" -ConsoleOut Log-Info -Message "Warning!! All environment proxy settings will be overwritten with the parameters passed in" -ConsoleOut Configure-EnvironmentProxy -NetworkProxy $Proxy -Bypass $ProxyBypass } Log-Info -Message "Installing and Running Azure Stack HCI Environment Checker" -ConsoleOut [Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor 3072; $environmentValidatorResult = [ErrorDetail]::Success if (-not [string]::IsNullOrEmpty($ArcGatewayID)) { $environmentValidatorResult = RunEnvironmentValidator -EnvironmentCheckerVersion $EnvironmentCheckerVersion -ArcGateway } else { $environmentValidatorResult = RunEnvironmentValidator -EnvironmentCheckerVersion $EnvironmentCheckerVersion } if ($environmentValidatorResult -ne [ErrorDetail]::Success -and (-Not $Force) ) { Log-Info -Message "Environment Validator failed so not installing the ARC agent" -Type Error -ConsoleOut throw "Environment Validator failed, so skipping ARC integration" } install-HypervModules -SkipErrors $Force Log-Info -Message "Starting AzStackHci ArcIntegration Initialization" -ConsoleOut $scrubbedParams = @{} foreach($psbp in $PSBoundParameters.GetEnumerator()) { if($psbp.Key -eq "ArmAccessToken") { continue } $scrubbedParams[$psbp.Key] = $psbp.Value } Write-AzStackHciHeader -invocation $MyInvocation -params $scrubbedParams -PassThru:$PassThru $ArcConnectionState = Check-NodeArcRegistrationStateScriptBlock if ($Cloud -eq "Azure.local") { $AltDownload = "https://artifacts.blob.$CloudFqdn/arc4server/AzureConnectedMachineAgent.msi" $AltHisEndpoint = "https://his.$CloudFqdn" $EndpointResourceManager = "https://armmanagement.$CloudFqdn" } else { $AltDownload = "https://download.microsoft.com/download/6/c/0/6c0775bc-9ed7-49af-9637-79653f783062/AzureConnectedMachineAgent.msi" $AltHisEndpoint = "" } #TODO: other validations related to OS Type and Version should happen here. # If the agent is already installed and not connected, we will re-install the agent again. This is like upgrade operation & "$PSScriptRoot\Classes\install_aszmagent_hci.ps1" -AltDownload $AltDownload -AltHisEndpoint $AltHisEndpoint; if ($LASTEXITCODE -ne 0) { exit 1; } # Run connect command $CorrelationID = New-Guid $machineName = [System.Net.Dns]::GetHostName() if (-not [string]::IsNullOrEmpty($Proxy)) { Log-Info -Message "Configuring proxy on agent : $($Proxy)" -ConsoleOut & "$env:ProgramW6432\AzureConnectedMachineAgent\azcmagent.exe" config set proxy.url $Proxy ; } $gatewaySupportedVersion = [System.Version]::Parse("1.40") $connectedMachineAgentVersion = & "$env:ProgramW6432\AzureConnectedMachineAgent\azcmagent.exe" --version $isGatewaySupported = [regex]::Match($connectedMachineAgentVersion, '\d+(\.\d+)+').Value -ge $gatewaySupportedVersion if ($PSCmdlet.ParameterSetName -eq "SPN") { Log-Info -Message "Connecting to Azure using SPN Credentials" -ConsoleOut Connect-AzAccount -ServicePrincipal -TenantId $TenantId -Credential $SpnCredential | out-null } elseif ($PSCmdlet.ParameterSetName -eq "ARMToken") { Log-Info -Message "Connecting to Azure using ARM Access Token" -ConsoleOut Connect-AzAccount -Environment $Cloud -Tenant $TenantID -AccessToken $ArmAccessToken -AccountId $AccountId -Subscription $SubscriptionID | out-null } Log-Info -Message "Connected to Azure successfully" -ConsoleOut Register-ResourceProviderIfRequired -ProviderNamespace "Microsoft.HybridCompute" Register-ResourceProviderIfRequired -ProviderNamespace "Microsoft.GuestConfiguration" Register-ResourceProviderIfRequired -ProviderNamespace "Microsoft.HybridConnectivity" Register-ResourceProviderIfRequired -ProviderNamespace "Microsoft.AzureStackHCI" if ($ArcConnectionState -ne [ErrorDetail]::NodeAlreadyArcEnabled) { Log-Info -Message "Connecting to Azure ARC agent " -ConsoleOut $argList = @{ "--resource-group" = $ResourceGroup; "--resource-name" = $machineName; "--tenant-id" = $TenantID; "--location" = $Region; "--subscription-id" = $SubscriptionID; "--cloud" = $Cloud; "--correlation-id" = $CorrelationID; } if ($isGatewaySupported -and -not [string]::IsNullOrEmpty($ArcGatewayID)) { $argList["--gateway-id"] = $ArcGatewayID; } if ($Cloud -eq "Azure.local") { $argList["--cloud"] = "AzureStackCloud"; $argList["--endpoint-resource-manager"] = $EndpointResourceManager; } if ($PSCmdlet.ParameterSetName -eq "SPN") { $argList["--service-principal-id"] = $SpnCredential.UserName; $argList["--service-principal-secret"] = $SpnCredential.GetNetworkCredential().Password; } elseif ($PSCmdlet.ParameterSetName -eq "ARMToken") { $argList["--access-token"] = $ArmAccessToken; } $argListAsArray = [System.Collections.ArrayList]@() foreach ($arg in $argList.GetEnumerator()) { $argListAsArray.Add($arg.Name) $argListAsArray.Add($arg.Value) } & "$env:ProgramW6432\AzureConnectedMachineAgent\azcmagent.exe" connect $argListAsArray; if ($LASTEXITCODE -ne 0) { Log-Info -Message "Azure ARC agent onboarding failed " -ConsoleOut throw "Arc agent onboarding failed, so erroring out, logs are present in C:\ProgramData\AzureConnectedMachineAgent\Log\azcmagent.log" } Log-Info -Message "Connected Azure ARC agent successfully " -ConsoleOut } else { Log-Info -Message "Node Already Arc Enabled, so skipping the arc registration" -ConsoleOut } if(-Not $SkipExtensionInstall) { Log-Info -Message "Configure to route all traffic via Arc proxy, if gateway setting is enabled " -ConsoleOut RouteAllTrafficViaArcProxy -NetworkProxy $(If ($Proxy) { $Proxy } else { "" }) -ProxyBypass $ProxyBypass Log-Info -Message "Installing AzureEdgeTelemetryAndDiagnostics Extension " -ConsoleOut Log-Info -Message "Installing AzureEdgeTelemetryAndDiagnostics Extension " -ConsoleOut $Settings = @{ "CloudName" = $Cloud; "RegionName" = $Region; "DeviceType" = "AzureEdge" } New-AzConnectedMachineExtension -Name "AzureEdgeTelemetryAndDiagnostics" -ResourceGroupName $ResourceGroup -MachineName $env:COMPUTERNAME -Location $Region -Publisher "Microsoft.AzureStack.Observability" -Settings $Settings -ExtensionType "TelemetryAndDiagnostics" -EnableAutomaticUpgrade -NoWait | out-null Log-Info -Message "Successfully triggered AzureEdgeTelemetryAndDiagnostics Extension installation " -ConsoleOut Start-Sleep -Seconds 60 Log-Info -Message "Installing DeviceManagement Extension " -ConsoleOut New-AzConnectedMachineExtension -Name "AzureEdgeDeviceManagement" -ResourceGroupName $ResourceGroup -MachineName $env:COMPUTERNAME -Location $Region -Publisher "Microsoft.Edge" -ExtensionType "DeviceManagementExtension" -NoWait | out-null Log-Info -Message "Successfully triggered DeviceManagementExtension installation " -ConsoleOut Start-Sleep -Seconds 60 Log-Info -Message "Installing LcmController Extension " -ConsoleOut New-AzConnectedMachineExtension -Name "AzureEdgeLifecycleManager" -ResourceGroupName $ResourceGroup -MachineName $env:COMPUTERNAME -Location $Region -Publisher "Microsoft.AzureStack.Orchestration" -ExtensionType "LcmController" -NoWait | out-null Log-Info -Message "Successfully triggered LCMController Extension installation " -ConsoleOut Start-Sleep -Seconds 60 if ($Cloud -ne "Azure.local") #EdgeRemoteSupport extension is not supported on Azure.local { Log-Info -Message "Installing EdgeRemoteSupport Extension " -ConsoleOut New-AzConnectedMachineExtension -Name "AzureEdgeRemoteSupport" -ResourceGroupName $ResourceGroup -MachineName $env:COMPUTERNAME -Location $Region -Publisher "Microsoft.AzureStack.Observability" -ExtensionType "EdgeRemoteSupport" -EnableAutomaticUpgrade -NoWait | out-null Log-Info -Message "Successfully triggered EdgeRemoteSupport Extension installation " -ConsoleOut } Log-Info -Message "ARC Extension installations Successfully triggered on the device." -ConsoleOut } Log-Info -Message "ARC Successfully enabled on the device." -ConsoleOut } else { $scrubbedParams = @{} foreach($psbp in $PSBoundParameters.GetEnumerator()) { if($psbp.Key -eq "ArmAccessToken") { continue } $scrubbedParams[$psbp.Key] = $psbp.Value } Write-AzStackHciHeader -invocation $MyInvocation -params $scrubbedParams -PassThru:$PassThru [Collections.Hashtable] $configHash = @{} if (-not [string]::IsNullOrEmpty($Proxy)) { Log-Info -Message "Constructing node config with proxy : $($Proxy) and bypass list: $($ProxyBypass)" -ConsoleOut $bypassList = @(); if ([string]::IsNullOrEmpty($ProxyBypass)) { # if the bypass list is not provided, read the system configured setting $ProxyBypass = Get-ConfiguredBypassList # if the system configured bypass setting is empty too, throw an error if ([string]::IsNullOrEmpty($ProxyBypass)) { throw "Bypass list cannot be empty. Please provide a valid bypass list in the format URL1;URL2;URL3 etc. The list needs to be separated by semicolon" } } # the bypass list should not contain any commas, it should only be separated by semicolon if ($ProxyBypass -like "*,*") { throw "Invalid bypass list. Please provide a valid bypass list in the format URL1;URL2;URL3 etc. The list needs to be separated by semicolon" } $bypassList = $ProxyBypass.Split(";") $configHash["WebProxy"] = @{ "ConnectionUri" = $Proxy "BypassList" = $bypassList } } if ($PSCmdlet.ParameterSetName -eq "SPN") { Log-Info -Message "Constructing node config using SPN Credentials" -ConsoleOut $configHash["ArcConfiguration"] = @{ "ArcMultiServerConfiguration" = @{ "resourceGroup" = $ResourceGroup "tenantId" = $TenantID "location" = $Region "subscriptionId" = $SubscriptionID "cloud" = $Cloud "gatewayId" = $ArcGatewayID "servicePrincipalClientId"= $SpnCredential.UserName "servicePrincipalSecret" = $SpnCredential.GetNetworkCredential().Password "AzureLocalCloudFQDN" = $CloudFqdn } } } elseif ($PSCmdlet.ParameterSetName -eq "ARMToken") { Log-Info -Message "Constructing node config using ARM Access Token" -ConsoleOut $configHash["ArcConfiguration"] = @{ "ArcMultiServerConfiguration" = @{ "resourceGroup" = $ResourceGroup "tenantId" = $TenantID "location" = $Region "subscriptionId" = $SubscriptionID "cloud" = $Cloud "gatewayId" = $ArcGatewayID "armAccessToken" = $ArmAccessToken "accountId" = $AccountID "AzureLocalCloudFQDN" = $CloudFqdn } } } $configHash | ConvertTo-Json | Out-File $tempConfigPath -Force $timeoutDuration = 1800 # 30 minutes # Set the polling interval (in seconds) $pollingInterval = 15 $elapsed = 0 try { Log-Info -Message "Triggering bootstrap on the device" -ConsoleOut $result = Start-ArcBootstrap -ConfigFilePath $tempConfigPath | ConvertFrom-Json do { # Check the Response Status $status = $result.Response.Status # If the status is Failed or Success, print the result and break the loop Log-Info -Message "" -ConsoleOut Log-Info -Message "Waiting for bootstrap to complete, Status: $status" -ConsoleOut Log-Info -Message "" -ConsoleOut # Display the response to the console foreach($response in $result.Response.DetailedResponse) { Show-BootstrapStatus -Response $response } Log-Info -Message "" -ConsoleOut if ($status -eq "Succeeded") { return $result } if ($status -eq "Failed") { Log-Info -Message "Bootstrap failed with: $($result.Response.Errors[0].ErrorMessage)" -ConsoleOut throw $result.Response.Errors[0].ErrorMessage } Start-Sleep -Seconds $pollingInterval $elapsed += $pollingInterval $result = Get-ArcBootstrapStatus } while ($elapsed -lt $timeoutDuration) } catch { Log-Info -Message "" -ConsoleOut Log-Info -Message "Elapsed time: $elapsed" -ConsoleOut if ($status -ne $null) { Log-Info -Message "Most recent status: $status" -ConsoleOut } if ($result -ne $null) { Log-Info -Message "Most recent status response: $result" -ConsoleOut } Log-Info -Message "Bootstrap failed: $($_.Exception.Message)" -ConsoleOut throw $_ } } } catch { Log-Info -Message "" -ConsoleOut Log-Info -Message "$($_.Exception.Message)" -ConsoleOut -Type Error Log-Info -Message "$($_.ScriptStackTrace)" -ConsoleOut -Type Error $cmdletFailed = $true throw $_ } finally { Log-Info -Message "Disconnect from Azure" Disconnect-AzAccount -ErrorAction SilentlyContinue | out-null $Script:ErrorActionPreference = 'SilentlyContinue' Remove-Item -Path $tempConfigPath -Force -ErrorAction SilentlyContinue | out-null Write-AzStackHciArcInstallerFooter -invocation $MyInvocation -Failed:$cmdletFailed -PassThru:$PassThru if ((Get-Command -Name Start-ArcBootstrap -ErrorAction SilentlyContinue)){ CollectBootstrapLogs } } } function CollectBootstrapLogs { $RetryDelaySec = 10 $maxWaitTime = 600 # 600 seconds = 10 mins $startTime = Get-Date $retryCount = -1 try { Start-ArcBootstrapSupportLogs -IncludeType Latest Log-Info -Message "Successfully triggered Arc boostrap support log collection. Waiting for $maxWaitTime seconds to complete." -ConsoleOut $arcBootstrapSupportLogsComplete = $false while ($((Get-Date) - $startTime).TotalSeconds -le $maxWaitTime) { try { $retryCount = $retryCount + 1 Log-Info -Message "Waiting for Arc bootstrap support logs to complete on '$NodeIP', retry count: $retryCount." -ConsoleOut $s = Get-ArcBootstrapSupportLogsStatus if ($s.Status -eq "Succeeded") { Log-Info -Message "Arc boostrap support log collection completed successfully." -ConsoleOut $arcBootstrapSupportLogsComplete = $true break } elseif ($s.Status -eq "Failed") { Log-Info -Message "Arc bootstrap support log collection failed" -ConsoleOut $arcBootstrapSupportLogsComplete = $true break } Log-Info -Message "Arc bootstrap support log collection status is $($s.Status). Sleep for $RetryDelaySec seconds." -ConsoleOut Start-Sleep -Seconds $RetryDelaySec } catch { $issue = ($_ | FL * | Out-String) Trace-Execution "Unable to query Arc bootstrap support log collection status due to $issue. Sleep for $RetryDelaySec seconds." Start-Sleep -Seconds $RetryDelaySec } } if (-not ($arcBootstrapSupportLogsComplete)) { throw "Arc bootstrap support log collection timed out after $maxWaitTime seconds." } } finally {} } function Remove-AzStackHciArcInitialization { <# .SYNOPSIS Perform AzStackHci ArcIntegration Initialization .DESCRIPTION Initializes ARC integration on Azure Stack HCI node .EXAMPLE PS C:\> Connect-AzAccount -Tenant $tenantID -Subscription $subscriptionID -DeviceCode PS C:\> $nodeNames = [string[]]("host1","host2","host3","host4") PS C:\> Invoke-AzStackHciArcIntegrationValidation -SubscriptionID $subscriptionID -ArcResourceGroupName $resourceGroupName -NodeNames $nodeNames .PARAMETER SubscriptionID Specifies the Azure Subscription to create the resource. Is Mandatory Paratmer .PARAMETER ResourceGroup TODO: This is not used anywhere. Remove it .PARAMETER TenantID Specifies the Azure TenantId.Required only if ARMAccessToken is used. .PARAMETER Cloud Specifies the Azure Environment. Valid values are AzureCloud, AzureChinaCloud, AzureUSGovernment. Required only if ARMAccessToken is used. .PARAMETER ArmAccessToken Specifies the ARM access token. Specifying this along with AccountId will avoid Azure interactive logon. If not specified, Azure Context is expected to be setup. .PARAMETER AccountID Specifies the Account Id. Specifying this along with ArmAccessToken will avoid Azure interactive logon. Required only if ARMAccessToken is used. .PARAMETER PassThru Return PSObject result. .PARAMETER OutputPath Directory path for log and report output. .PARAMETER CleanReport Remove all previous progress and create a clean report. .INPUTS Inputs (if any) .OUTPUTS Output (if any) #> [CmdletBinding(DefaultParametersetName='AZContext')] param ( [Parameter(ParameterSetName='SPN', Mandatory = $true, HelpMessage = "Azure Environment used for HCI ARC Integration")] [Parameter(ParameterSetName='ARMToken', Mandatory = $true, HelpMessage = "Azure Environment used for HCI ARC Integration")] [string] $SubscriptionID, #TODO: should we do a validation of if the resource group is created or should we create the RG ? [Parameter(ParameterSetName='SPN', Mandatory = $true, HelpMessage = "Azure Environment used for HCI ARC Integration")] [Parameter(ParameterSetName='ARMToken', Mandatory = $true, HelpMessage = "Azure Tenant used for HCI ARC Integration")] [ValidateScript({ if ($_ -match '^[a-zA-Z0-9_()\-\.]{1,89}[a-zA-Z0-9_()\-]$') { $true } else { throw "ResourceGroup is invalid. The requirements for resource group names are: 1.Between 1 and 90 characters long. 2.Alphanumeric characters, underscores, parentheses, hyphens and periods. 3.Cannot end with a period." } })] [string] $ResourceGroup, [Parameter(ParameterSetName='SPN', Mandatory = $true, HelpMessage = "Azure Environment used for HCI ARC Integration")] [Parameter(ParameterSetName='ARMToken', Mandatory = $true, HelpMessage = "Azure Subscription used for HCI ARC Integration")] [string] $TenantID, # AzureCloud , AzureUSGovernment , AzureChinaCloud [Parameter(ParameterSetName='SPN', Mandatory = $true, HelpMessage = "Specifies the Azure Environment. Azure Valid values are AzureCloud, AzureChinaCloud, AzureUSGovernment")] [Parameter(ParameterSetName='ARMToken', Mandatory = $true, HelpMessage = "Specifies the Azure Environment. Azure Valid values are AzureCloud, AzureChinaCloud, AzureUSGovernment")] [string] $Cloud, [Parameter(ParameterSetName='ARMToken', Mandatory = $true, HelpMessage = "ARM Access Token used for HCI ARC Integration")] [string] $ArmAccessToken, [Parameter(ParameterSetName='ARMToken', Mandatory = $true, HelpMessage = "Account ID used for HCI ARC Integration")] [string] $AccountID, [Parameter(ParameterSetName='SPN', Mandatory = $true, HelpMessage = "SPN credential used for onboarding ARC machine")] [System.Management.Automation.PSCredential] $SpnCredential, [Parameter(ParameterSetName='SPN', Mandatory=$false)] [Parameter(ParameterSetName='ARMToken', Mandatory = $false, HelpMessage = "Use to force clean the device , even if the cloud side clean up fails")] [switch] $Force, [Parameter(ParameterSetName='SPN', Mandatory=$false)] [Parameter(ParameterSetName='ARMToken', Mandatory = $false, HelpMessage = "Directory path for log and report output")] [string]$OutputPath ) try { $script:ErrorActionPreference = 'Stop' Set-AzStackHciOutputPath -Path $OutputPath [Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor 3072; Log-Info -Message "Starting Arc Cleanup" -ConsoleOut $ArcConnectionState = Check-NodeArcRegistrationStateScriptBlock if ($PSCmdlet.ParameterSetName -eq "SPN") { Log-info -Message "Connecting to Azure with SPN" -ConsoleOut Connect-AzAccount -ServicePrincipal -TenantId $TenantId -Credential $SpnCredential Log-info -Message "Successfully connected to Azure with SPN" -ConsoleOut if ($ArcConnectionState -eq [ErrorDetail]::NodeAlreadyArcEnabled) { try { Log-Info -Message "Removing Arc Extensions" -ConsoleOut #TODO: enable Debug logs on Azure cmdlets Get-AzConnectedMachineExtension -ResourceGroupName "$ResourceGroup" -MachineName $ENV:COMPUTERNAME | Remove-AzConnectedMachineExtension -NoWait Log-Info -Message "Removed Arc Extensions successfully" -ConsoleOut & "$env:ProgramW6432\AzureConnectedMachineAgent\azcmagent.exe" disconnect --service-principal-id "$SpnCredential.UserName" --service-principal-secret "$SpnCredential.GetNetworkCredential().Password" ; Log-Info -Message "successfully disconnected ARC agent" -ConsoleOut } catch { & "$env:ProgramW6432\AzureConnectedMachineAgent\azcmagent.exe" disconnect --force-local-only; #TODO: delete all the extension folders } } else{ Log-Info -Message "Node was not ARC enabled so not disconnecting from ARC" -ConsoleOut } } elseif ($PSCmdlet.ParameterSetName -eq "ARMToken") { Log-Info -Message "Connecting to Azure with ARMAccess Token" -ConsoleOut Connect-AzAccount -Environment $Cloud -Tenant $TenantID -AccessToken $ArmAccessToken -AccountId $AccountId -Subscription $SubscriptionID | out-null Log-Info -Message "Successfully connected to Azure with ARM Token" -ConsoleOut if ($ArcConnectionState -eq [ErrorDetail]::NodeAlreadyArcEnabled) { try { Log-Info -Message "Removing Arc Extensions" -ConsoleOut Get-AzConnectedMachineExtension -ResourceGroupName "$ResourceGroup" -MachineName $ENV:COMPUTERNAME | Remove-AzConnectedMachineExtension -NoWait & "$env:ProgramW6432\AzureConnectedMachineAgent\azcmagent.exe" disconnect --access-token "$ArmAccessToken"; Log-Info -Message "successfully disconnected ARC agent" -ConsoleOut } catch { & "$env:ProgramW6432\AzureConnectedMachineAgent\azcmagent.exe" disconnect --force-local-only; #TODO: delete all the extension folders } } else{ Log-Info -Message "Node was not ARC enabled, so not removing ARC agent" -ConsoleOut } } } catch { Log-Info -Message "" -ConsoleOut Log-Info -Message "$($_.Exception.Message)" -ConsoleOut -Type Error Log-Info -Message "$($_.ScriptStackTrace)" -ConsoleOut -Type Error $cmdletFailed = $true throw $_ } finally { Disconnect-AzAccount -ErrorAction SilentlyContinue | out-null $Script:ErrorActionPreference = 'SilentlyContinue' } } function install-HypervModules{ param ( [bool] $SkipErrors ) $status = Get-WindowsOptionalFeature -Online -FeatureName:Microsoft-Hyper-V if ($status.State -ne "Enabled") { if($SkipErrors) { Log-Info -Message "Hyper-v feature is not enabled. Continuing since 'Force' is configured." -ConsoleOut } else { throw "Windows Feature 'Microsoft-Hyper-V' is not enabled. Cannot proceed." } } if (($state.RestartRequired -eq "Possible") -or ($state.RestartRequired -eq "Required")) { if($SkipErrors) { Log-Info -Message "Hyper-v feature requires a node restart, please restart the node using Restart-Computer -Force" -ConsoleOut } else { throw "Windows Feature 'Microsoft-Hyper-V' requires a node restart to be enabled. Please run Restart-Computer -Force" } } try { Log-Info -Message "Installing Hyper-V Management Tools" -ConsoleOut Install-WindowsFeature -Name Hyper-V -IncludeManagementTools | Out-Null Log-Info -Message "Successfully installed Hyper-V Management Tools" } catch { Log-Info -Message "" -ConsoleOut Log-Info -Message "$($_.Exception.Message)" -ConsoleOut -Type Error Log-Info -Message "$($_.ScriptStackTrace)" -ConsoleOut -Type Error } } # Method to Get the object id from the ARC Imds endpoint function GetObjectIdFromArcMachine { try { $headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" $headers.Add("metadata", "true") $headers.Add("UseDefaultCredentials","true") $response = Invoke-WebRequest -Uri "http://localhost:40342/metadata/instance/compute?api-version=2020-06-01" -Method GET -Headers $headers -UseBasicParsing $content = $response.Content | ConvertFrom-Json Log-Info -Message "Successfully got the content from IMDS endpoint" -ConsoleOut $arcResource = Get-AzResource -ResourceId $content.resourceId $objectId = $arcResource.Identity.PrincipalId Log-Info -Message "Successfully got Object Id for Arc Installation $objectId" -ConsoleOut return $objectId } catch { Log-Info -Message "" -ConsoleOut Log-Info -Message "$($_.Exception.Message)" -ConsoleOut -Type Error Log-Info -Message "$($_.ScriptStackTrace)" -ConsoleOut -Type Error } } function RunEnvironmentValidator { [CmdletBinding()] param ( [System.Version] $EnvironmentCheckerVersion, [switch] $ArcGateway ) try { if ([string]::IsNullOrEmpty($EnvironmentCheckerVersion)) { Install-Module -Name AzStackHci.EnvironmentChecker -Repository PSGallery -Force } else { Install-Module -Name AzStackHci.EnvironmentChecker -Repository PSGallery -Force -RequiredVersion $EnvironmentCheckerVersion } $ENV:EnvChkrOp = 'ArcInitialization' $isGatewayParameterSupported = (Get-Command -Name Invoke-AzStackHciConnectivityValidation).Parameters.ContainsKey('ARCGateway') $res = @() if ($isGatewayParameterSupported -and $ArcGateway) { Log-Info -Message "Running Environment Validation checks with ARC Gateway parameter, $($EnvironmentCheckerVersion)" -ConsoleOut $res = Invoke-AzStackHciConnectivityValidation -PassThru -ARCGateway } else { Log-Info -Message "Running Environment Validation checks without ARC Gateway parameter, $($EnvironmentCheckerVersion)" -ConsoleOut $res = Invoke-AzStackHciConnectivityValidation -PassThru $res = $res | Where-object Name -ne 'AzStackHci_Connectivity_Proxy_Settings_Consistency' } $successfulTests = $res | Where-Object { $_.Status -in @("Succeeded","SUCCESS")} if ($res.Count -eq $successfulTests.Count){ Log-Info -Message "All the environment validation checks succeeded" -ConsoleOut return [ErrorDetail]::Success } else { $failedTests = $res | Where-Object { $_.Status -notin @("Succeeded","SUCCESS")} $criticalFailedTests = $failedTests | Where-Object { $_.Severity -eq "Critical"} if( $criticalFailedTests.Count -gt 0) { Log-Info -Message "Critical environment validations failed, Failed Tests are shown below" -ConsoleOut $criticalFailedTests | Where-Object { $msg = $_ | Format-List | Out-String ; Log-Info -Message $msg -ConsoleOut } return [ErrorDetail]::EnvironmentValidationFailed }else { Log-Info -Message "Non-Critical environment validations failed, Failed Tests are shown below" -ConsoleOut $failedTests | Where-Object { $msg = $_ | Format-List | Out-String ; Log-Info -Message $msg -ConsoleOut } return [ErrorDetail]::Success } } } catch { Log-Info -Message "" -ConsoleOut Log-Info -Message "$($_.Exception.Message)" -ConsoleOut -Type Error Log-Info -Message "$($_.ScriptStackTrace)" -ConsoleOut -Type Error return [ErrorDetail]::EnvironmentValidationFailed } return [ErrorDetail]::EnvironmentValidationFailed } enum ErrorDetail { Unused; PermissionsMissing; Success; NodeAlreadyArcEnabled; EnvironmentValidationFailed } Export-ModuleMember -Function Invoke-AzStackHciArcInitialization Export-ModuleMember -Function Remove-AzStackHciArcInitialization # SIG # Begin signature block # MIIoKgYJKoZIhvcNAQcCoIIoGzCCKBcCAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCDwzhwNz1YXQAUp # bPt0y8qHXgvBc0FnDooaWcPVvMuWq6CCDXYwggX0MIID3KADAgECAhMzAAADrzBA # DkyjTQVBAAAAAAOvMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD # VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p # bmcgUENBIDIwMTEwHhcNMjMxMTE2MTkwOTAwWhcNMjQxMTE0MTkwOTAwWjB0MQsw # CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u # ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB # AQDOS8s1ra6f0YGtg0OhEaQa/t3Q+q1MEHhWJhqQVuO5amYXQpy8MDPNoJYk+FWA # hePP5LxwcSge5aen+f5Q6WNPd6EDxGzotvVpNi5ve0H97S3F7C/axDfKxyNh21MG # 0W8Sb0vxi/vorcLHOL9i+t2D6yvvDzLlEefUCbQV/zGCBjXGlYJcUj6RAzXyeNAN # xSpKXAGd7Fh+ocGHPPphcD9LQTOJgG7Y7aYztHqBLJiQQ4eAgZNU4ac6+8LnEGAL # go1ydC5BJEuJQjYKbNTy959HrKSu7LO3Ws0w8jw6pYdC1IMpdTkk2puTgY2PDNzB # tLM4evG7FYer3WX+8t1UMYNTAgMBAAGjggFzMIIBbzAfBgNVHSUEGDAWBgorBgEE # AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQURxxxNPIEPGSO8kqz+bgCAQWGXsEw # RQYDVR0RBD4wPKQ6MDgxHjAcBgNVBAsTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEW # MBQGA1UEBRMNMjMwMDEyKzUwMTgyNjAfBgNVHSMEGDAWgBRIbmTlUAXTgqoXNzci # tW2oynUClTBUBgNVHR8ETTBLMEmgR6BFhkNodHRwOi8vd3d3Lm1pY3Jvc29mdC5j # b20vcGtpb3BzL2NybC9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDctMDguY3JsMGEG # CCsGAQUFBwEBBFUwUzBRBggrBgEFBQcwAoZFaHR0cDovL3d3dy5taWNyb3NvZnQu # Y29tL3BraW9wcy9jZXJ0cy9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDctMDguY3J0 # MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggIBAISxFt/zR2frTFPB45Yd # mhZpB2nNJoOoi+qlgcTlnO4QwlYN1w/vYwbDy/oFJolD5r6FMJd0RGcgEM8q9TgQ # 2OC7gQEmhweVJ7yuKJlQBH7P7Pg5RiqgV3cSonJ+OM4kFHbP3gPLiyzssSQdRuPY # 1mIWoGg9i7Y4ZC8ST7WhpSyc0pns2XsUe1XsIjaUcGu7zd7gg97eCUiLRdVklPmp # XobH9CEAWakRUGNICYN2AgjhRTC4j3KJfqMkU04R6Toyh4/Toswm1uoDcGr5laYn # TfcX3u5WnJqJLhuPe8Uj9kGAOcyo0O1mNwDa+LhFEzB6CB32+wfJMumfr6degvLT # e8x55urQLeTjimBQgS49BSUkhFN7ois3cZyNpnrMca5AZaC7pLI72vuqSsSlLalG # OcZmPHZGYJqZ0BacN274OZ80Q8B11iNokns9Od348bMb5Z4fihxaBWebl8kWEi2O # PvQImOAeq3nt7UWJBzJYLAGEpfasaA3ZQgIcEXdD+uwo6ymMzDY6UamFOfYqYWXk # ntxDGu7ngD2ugKUuccYKJJRiiz+LAUcj90BVcSHRLQop9N8zoALr/1sJuwPrVAtx # HNEgSW+AKBqIxYWM4Ev32l6agSUAezLMbq5f3d8x9qzT031jMDT+sUAoCw0M5wVt # CUQcqINPuYjbS1WgJyZIiEkBMIIHejCCBWKgAwIBAgIKYQ6Q0gAAAAAAAzANBgkq # hkiG9w0BAQsFADCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24x # EDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlv # bjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5 # IDIwMTEwHhcNMTEwNzA4MjA1OTA5WhcNMjYwNzA4MjEwOTA5WjB+MQswCQYDVQQG # EwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwG # A1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSgwJgYDVQQDEx9NaWNyb3NvZnQg # Q29kZSBTaWduaW5nIFBDQSAyMDExMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC # CgKCAgEAq/D6chAcLq3YbqqCEE00uvK2WCGfQhsqa+laUKq4BjgaBEm6f8MMHt03 # a8YS2AvwOMKZBrDIOdUBFDFC04kNeWSHfpRgJGyvnkmc6Whe0t+bU7IKLMOv2akr # rnoJr9eWWcpgGgXpZnboMlImEi/nqwhQz7NEt13YxC4Ddato88tt8zpcoRb0Rrrg # OGSsbmQ1eKagYw8t00CT+OPeBw3VXHmlSSnnDb6gE3e+lD3v++MrWhAfTVYoonpy # 4BI6t0le2O3tQ5GD2Xuye4Yb2T6xjF3oiU+EGvKhL1nkkDstrjNYxbc+/jLTswM9 # sbKvkjh+0p2ALPVOVpEhNSXDOW5kf1O6nA+tGSOEy/S6A4aN91/w0FK/jJSHvMAh # dCVfGCi2zCcoOCWYOUo2z3yxkq4cI6epZuxhH2rhKEmdX4jiJV3TIUs+UsS1Vz8k # A/DRelsv1SPjcF0PUUZ3s/gA4bysAoJf28AVs70b1FVL5zmhD+kjSbwYuER8ReTB # w3J64HLnJN+/RpnF78IcV9uDjexNSTCnq47f7Fufr/zdsGbiwZeBe+3W7UvnSSmn # Eyimp31ngOaKYnhfsi+E11ecXL93KCjx7W3DKI8sj0A3T8HhhUSJxAlMxdSlQy90 # lfdu+HggWCwTXWCVmj5PM4TasIgX3p5O9JawvEagbJjS4NaIjAsCAwEAAaOCAe0w # ggHpMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBRIbmTlUAXTgqoXNzcitW2o # ynUClTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMCAYYwDwYD # VR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBRyLToCMZBDuRQFTuHqp8cx0SOJNDBa # BgNVHR8EUzBRME+gTaBLhklodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2Ny # bC9wcm9kdWN0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFfMDNfMjIuY3JsMF4GCCsG # AQUFBwEBBFIwUDBOBggrBgEFBQcwAoZCaHR0cDovL3d3dy5taWNyb3NvZnQuY29t # L3BraS9jZXJ0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFfMDNfMjIuY3J0MIGfBgNV # HSAEgZcwgZQwgZEGCSsGAQQBgjcuAzCBgzA/BggrBgEFBQcCARYzaHR0cDovL3d3 # dy5taWNyb3NvZnQuY29tL3BraW9wcy9kb2NzL3ByaW1hcnljcHMuaHRtMEAGCCsG # AQUFBwICMDQeMiAdAEwAZQBnAGEAbABfAHAAbwBsAGkAYwB5AF8AcwB0AGEAdABl # AG0AZQBuAHQALiAdMA0GCSqGSIb3DQEBCwUAA4ICAQBn8oalmOBUeRou09h0ZyKb # C5YR4WOSmUKWfdJ5DJDBZV8uLD74w3LRbYP+vj/oCso7v0epo/Np22O/IjWll11l # hJB9i0ZQVdgMknzSGksc8zxCi1LQsP1r4z4HLimb5j0bpdS1HXeUOeLpZMlEPXh6 # I/MTfaaQdION9MsmAkYqwooQu6SpBQyb7Wj6aC6VoCo/KmtYSWMfCWluWpiW5IP0 # wI/zRive/DvQvTXvbiWu5a8n7dDd8w6vmSiXmE0OPQvyCInWH8MyGOLwxS3OW560 # STkKxgrCxq2u5bLZ2xWIUUVYODJxJxp/sfQn+N4sOiBpmLJZiWhub6e3dMNABQam # ASooPoI/E01mC8CzTfXhj38cbxV9Rad25UAqZaPDXVJihsMdYzaXht/a8/jyFqGa # J+HNpZfQ7l1jQeNbB5yHPgZ3BtEGsXUfFL5hYbXw3MYbBL7fQccOKO7eZS/sl/ah # XJbYANahRr1Z85elCUtIEJmAH9AAKcWxm6U/RXceNcbSoqKfenoi+kiVH6v7RyOA # 9Z74v2u3S5fi63V4GuzqN5l5GEv/1rMjaHXmr/r8i+sLgOppO6/8MO0ETI7f33Vt # Y5E90Z1WTk+/gFcioXgRMiF670EKsT/7qMykXcGhiJtXcVZOSEXAQsmbdlsKgEhr # /Xmfwb1tbWrJUnMTDXpQzTGCGgowghoGAgEBMIGVMH4xCzAJBgNVBAYTAlVTMRMw # EQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVN # aWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNp # Z25pbmcgUENBIDIwMTECEzMAAAOvMEAOTKNNBUEAAAAAA68wDQYJYIZIAWUDBAIB # BQCgga4wGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEO # MAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIJiWu+6N10qS06B+LsR7xqt0 # R8K3VL12aem1KecXT7wbMEIGCisGAQQBgjcCAQwxNDAyoBSAEgBNAGkAYwByAG8A # cwBvAGYAdKEagBhodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20wDQYJKoZIhvcNAQEB # BQAEggEAXYl5X19Xp3d8QS0nI7SiqJQ71KBzYZSQry78zHUZYwZ6z7pROvQtIYEL # ep1xYc30wedq1I+GgIhSUf7UoBXXpUXSo46wikskVcSHT2mS8AYCOagdwm5xD2mI # KExbU0tqXIUPQxBnhy/9AnYF+JRJVI3c6E4xzfl5EaIwCbWdqXoL67b1vigblnAT # KfAcUO1zEdIxpEZ8Wih1C1ZPY2y+Vt/CkynPUjnEFfs4O5FGcgEcKBi1bH5qT0Ch # o7nJYWxL9JMxaOpoHjWo6a9g5PPmozX/q3SEHUlPZsJhaz3BkNgYJyE5YUECu5kH # sc4lpKcx05ZpGv1sQM4uL3BiqvlR96GCF5QwgheQBgorBgEEAYI3AwMBMYIXgDCC # F3wGCSqGSIb3DQEHAqCCF20wghdpAgEDMQ8wDQYJYIZIAWUDBAIBBQAwggFSBgsq # hkiG9w0BCRABBKCCAUEEggE9MIIBOQIBAQYKKwYBBAGEWQoDATAxMA0GCWCGSAFl # AwQCAQUABCD3QHiZS0JcNoDnLMgrTi2aEOn0jB/JyEcPLHe/rf5z3wIGZwgPj2xU # GBMyMDI0MTAxNDIyNDQ1OC4xMzlaMASAAgH0oIHRpIHOMIHLMQswCQYDVQQGEwJV # UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UE # ChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSUwIwYDVQQLExxNaWNyb3NvZnQgQW1l # cmljYSBPcGVyYXRpb25zMScwJQYDVQQLEx5uU2hpZWxkIFRTUyBFU046RTAwMi0w # NUUwLUQ5NDcxJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2Wg # ghHqMIIHIDCCBQigAwIBAgITMwAAAe4F0wIwspqdpwABAAAB7jANBgkqhkiG9w0B # AQsFADB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UE # BxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYD # VQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDAeFw0yMzEyMDYxODQ1 # NDRaFw0yNTAzMDUxODQ1NDRaMIHLMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2Fz # aGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENv # cnBvcmF0aW9uMSUwIwYDVQQLExxNaWNyb3NvZnQgQW1lcmljYSBPcGVyYXRpb25z # MScwJQYDVQQLEx5uU2hpZWxkIFRTUyBFU046RTAwMi0wNUUwLUQ5NDcxJTAjBgNV # BAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2UwggIiMA0GCSqGSIb3DQEB # AQUAA4ICDwAwggIKAoICAQC+8byl16KEia8xKS4vVL7REOOR7LzYCLXEtWgeqyOV # lrzuEz+AoCa4tBGESjbHTXECeMOwP9TPeKaKalfTU5XSGjpJhpGx59fxMJoTYWPz # zD0O2RAlyBmOBBmiLDXRDQJL1RtuAjvCiLulVQeiPI8V7+HhTR391TbC1beSxwXf # dKJqY1onjDawqDJAmtwsA/gmqXgHwF9fZWcwKSuXiZBTbU5fcm3bhhlRNw5d04Ld # 15ZWzVl/VDp/iRerGo2Is/0Wwn/a3eGOdHrvfwIbfk6lVqwbNQE11Oedn2uvRjKW # EwerXL70OuDZ8vLzxry0yEdvQ8ky+Vfq8mfEXS907Y7rN/HYX6cCsC2soyXG3OwC # tLA7o0/+kKJZuOrD5HUrSz3kfqgDlmWy67z8ZZPjkiDC1dYW1jN77t5iSl5Wp1HK # Bp7JU8RiRI+vY2i1cb5X2REkw3WrNW/jbofXEs9t4bgd+yU8sgKn9MtVnQ65s6QG # 72M/yaUZG2HMI31tm9mooH29vPBO9jDMOIu0LwzUTkIWflgd/vEWfTNcPWEQj7fs # WuSoVuJ3uBqwNmRSpmQDzSfMaIzuys0pvV1jFWqtqwwCcaY/WXsb/axkxB/zCTdH # SBUJ8Tm3i4PM9skiunXY+cSqH58jWkpHbbLA3Ofss7e+JbMjKmTdcjmSkb5oN8qU # 1wIDAQABo4IBSTCCAUUwHQYDVR0OBBYEFBCIzT8a2dwgnr37xd+2v1/cdqYIMB8G # A1UdIwQYMBaAFJ+nFV0AXmJdg/Tl0mWnG1M1GelyMF8GA1UdHwRYMFYwVKBSoFCG # Tmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY3Jvc29mdCUy # MFRpbWUtU3RhbXAlMjBQQ0ElMjAyMDEwKDEpLmNybDBsBggrBgEFBQcBAQRgMF4w # XAYIKwYBBQUHMAKGUGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY2Vy # dHMvTWljcm9zb2Z0JTIwVGltZS1TdGFtcCUyMFBDQSUyMDIwMTAoMSkuY3J0MAwG # A1UdEwEB/wQCMAAwFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwgwDgYDVR0PAQH/BAQD # AgeAMA0GCSqGSIb3DQEBCwUAA4ICAQB3ZyAva2EKOWSVpBnYkzX8f8GZjaOs577F # 9o14Anh9lKy6tS34wXoPXEyQp1v1iI7rJzZVG7rpUznay2n9csfn3p6y7kYkHqtS # ugCGmTiiBkwhFfSByKPI08MklgvJvKTZb673yGfpFwPjQwZeI6EPj/OAtpYkT7IU # XqMki1CRMJKgeY4wURCccIujdWRkoVv4J3q/87KE0qPQmAR9fqMNxjI3ZClVxA4w # iM3tNVlRbF9SgpOnjVo3P/I5p8Jd41hNSVCx/8j3qM7aLSKtDzOEUNs+ZtjhznmZ # gUd7/AWHDhwBHdL57TI9h7niZkfOZOXncYsKxG4gryTshU6G6sAYpbqdME/+/g1u # er7VGIHUtLq3W0Anm8lAfS9PqthskZt54JF28CHdsFq/7XVBtFlxL/KgcQylJNni # a+anixUG60yUDt3FMGSJI34xG9NHsz3BpqSWueGtJhQ5ZN0K8ju0vNVgF+Dv05si # rPg0ftSKf9FVECp93o8ogF48jh8CT/B32lz1D6Truk4Ezcw7E1OhtOMf7DHgPMWf # 6WOdYnf+HaSJx7ZTXCJsW5oOkM0sLitxBpSpGcj2YjnNznCpsEPZat0h+6d7ulRa # WR5RHAUyFFQ9jRa7KWaNGdELTs+nHSlYjYeQpK5QSXjigdKlLQPBlX+9zOoGAJho # Zfrpjq4nQDCCB3EwggVZoAMCAQICEzMAAAAVxedrngKbSZkAAAAAABUwDQYJKoZI # hvcNAQELBQAwgYgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAw # DgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24x # MjAwBgNVBAMTKU1pY3Jvc29mdCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAy # MDEwMB4XDTIxMDkzMDE4MjIyNVoXDTMwMDkzMDE4MzIyNVowfDELMAkGA1UEBhMC # VVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNV # BAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRp # bWUtU3RhbXAgUENBIDIwMTAwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC # AQDk4aZM57RyIQt5osvXJHm9DtWC0/3unAcH0qlsTnXIyjVX9gF/bErg4r25Phdg # M/9cT8dm95VTcVrifkpa/rg2Z4VGIwy1jRPPdzLAEBjoYH1qUoNEt6aORmsHFPPF # dvWGUNzBRMhxXFExN6AKOG6N7dcP2CZTfDlhAnrEqv1yaa8dq6z2Nr41JmTamDu6 # GnszrYBbfowQHJ1S/rboYiXcag/PXfT+jlPP1uyFVk3v3byNpOORj7I5LFGc6XBp # Dco2LXCOMcg1KL3jtIckw+DJj361VI/c+gVVmG1oO5pGve2krnopN6zL64NF50Zu # yjLVwIYwXE8s4mKyzbnijYjklqwBSru+cakXW2dg3viSkR4dPf0gz3N9QZpGdc3E # XzTdEonW/aUgfX782Z5F37ZyL9t9X4C626p+Nuw2TPYrbqgSUei/BQOj0XOmTTd0 # lBw0gg/wEPK3Rxjtp+iZfD9M269ewvPV2HM9Q07BMzlMjgK8QmguEOqEUUbi0b1q # GFphAXPKZ6Je1yh2AuIzGHLXpyDwwvoSCtdjbwzJNmSLW6CmgyFdXzB0kZSU2LlQ # +QuJYfM2BjUYhEfb3BvR/bLUHMVr9lxSUV0S2yW6r1AFemzFER1y7435UsSFF5PA # PBXbGjfHCBUYP3irRbb1Hode2o+eFnJpxq57t7c+auIurQIDAQABo4IB3TCCAdkw # EgYJKwYBBAGCNxUBBAUCAwEAATAjBgkrBgEEAYI3FQIEFgQUKqdS/mTEmr6CkTxG # NSnPEP8vBO4wHQYDVR0OBBYEFJ+nFV0AXmJdg/Tl0mWnG1M1GelyMFwGA1UdIARV # MFMwUQYMKwYBBAGCN0yDfQEBMEEwPwYIKwYBBQUHAgEWM2h0dHA6Ly93d3cubWlj # cm9zb2Z0LmNvbS9wa2lvcHMvRG9jcy9SZXBvc2l0b3J5Lmh0bTATBgNVHSUEDDAK # BggrBgEFBQcDCDAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMC # AYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBTV9lbLj+iiXGJo0T2UkFvX # zpoYxDBWBgNVHR8ETzBNMEugSaBHhkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20v # cGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXRfMjAxMC0wNi0yMy5jcmwwWgYI # KwYBBQUHAQEETjBMMEoGCCsGAQUFBzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5j # b20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNydDANBgkqhkiG # 9w0BAQsFAAOCAgEAnVV9/Cqt4SwfZwExJFvhnnJL/Klv6lwUtj5OR2R4sQaTlz0x # M7U518JxNj/aZGx80HU5bbsPMeTCj/ts0aGUGCLu6WZnOlNN3Zi6th542DYunKmC # VgADsAW+iehp4LoJ7nvfam++Kctu2D9IdQHZGN5tggz1bSNU5HhTdSRXud2f8449 # xvNo32X2pFaq95W2KFUn0CS9QKC/GbYSEhFdPSfgQJY4rPf5KYnDvBewVIVCs/wM # nosZiefwC2qBwoEZQhlSdYo2wh3DYXMuLGt7bj8sCXgU6ZGyqVvfSaN0DLzskYDS # PeZKPmY7T7uG+jIa2Zb0j/aRAfbOxnT99kxybxCrdTDFNLB62FD+CljdQDzHVG2d # Y3RILLFORy3BFARxv2T5JL5zbcqOCb2zAVdJVGTZc9d/HltEAY5aGZFrDZ+kKNxn # GSgkujhLmm77IVRrakURR6nxt67I6IleT53S0Ex2tVdUCbFpAUR+fKFhbHP+Crvs # QWY9af3LwUFJfn6Tvsv4O+S3Fb+0zj6lMVGEvL8CwYKiexcdFYmNcP7ntdAoGokL # jzbaukz5m/8K6TT4JDVnK+ANuOaMmdbhIurwJ0I9JZTmdHRbatGePu1+oDEzfbzL # 6Xu/OHBE0ZDxyKs6ijoIYn/ZcGNTTY3ugm2lBRDBcQZqELQdVTNYs6FwZvKhggNN # MIICNQIBATCB+aGB0aSBzjCByzELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hp # bmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jw # b3JhdGlvbjElMCMGA1UECxMcTWljcm9zb2Z0IEFtZXJpY2EgT3BlcmF0aW9uczEn # MCUGA1UECxMeblNoaWVsZCBUU1MgRVNOOkUwMDItMDVFMC1EOTQ3MSUwIwYDVQQD # ExxNaWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2aWNloiMKAQEwBwYFKw4DAhoDFQCI # o6bVNvflFxbUWCDQ3YYKy6O+k6CBgzCBgKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYD # VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1w # IFBDQSAyMDEwMA0GCSqGSIb3DQEBCwUAAgUA6rfTvDAiGA8yMDI0MTAxNDE3MzAz # NloYDzIwMjQxMDE1MTczMDM2WjB0MDoGCisGAQQBhFkKBAExLDAqMAoCBQDqt9O8 # AgEAMAcCAQACAgomMAcCAQACAhOnMAoCBQDquSU8AgEAMDYGCisGAQQBhFkKBAIx # KDAmMAwGCisGAQQBhFkKAwKgCjAIAgEAAgMHoSChCjAIAgEAAgMBhqAwDQYJKoZI # hvcNAQELBQADggEBAHzINoZAJi9oeJZmlEX50LK7ujp55bgqRVOWswFX0cVXVIQo # LQQYeRRLsaAUnDgsX7Zxher8+zF9ajbp2ynN7BFvngP19JaKHIJUII+mbBWXPphP # n7fdTuSYhkrEGOOY/AwZmyeiiRswkVPxjEKKzrgp+lE5MD3vAn0oh/UbObqL4AiG # V+T21dQT6oBYpD6Y86zqmINEnYWb7tZO4Pvc/LXVoA3GTIsCmdXd1dqUvYZGLKoL # 0FA88mk87ztLODiAjoHknVvA1N8aRHEY4eZg3HwflrnZfGoGpNtYnN1ZnmuM80NQ # eXd9uT7WEHcmF4Qh5Xsg1n+yh5RkOFgz638dsvAxggQNMIIECQIBATCBkzB8MQsw # CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u # ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNy # b3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMAITMwAAAe4F0wIwspqdpwABAAAB7jAN # BglghkgBZQMEAgEFAKCCAUowGgYJKoZIhvcNAQkDMQ0GCyqGSIb3DQEJEAEEMC8G # CSqGSIb3DQEJBDEiBCBQMwmno3m1JX3BuyHd149hXRnN/E7GIgi3IwBmfnlrUDCB # +gYLKoZIhvcNAQkQAi8xgeowgecwgeQwgb0EIE9QdxSVhfq+Vdf+DPs+5EIkBz9o # CS/OQflHkVRhfjAhMIGYMIGApH4wfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldh # c2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBD # b3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIw # MTACEzMAAAHuBdMCMLKanacAAQAAAe4wIgQgycOH8EwPv/ttcv5fBaNKzDm57Dw1 # I7vGzFGGT2DUknEwDQYJKoZIhvcNAQELBQAEggIATMpPAlQhB6X3SziiKvCf5+Op # Zj1OcjMxeeXbIzSTmacUT5wCuqhH5JvVh6FhgjqQp69lO4RlZ9RYRzxiJG9U7S7P # 6+ODtK1C4Zu2+pv5gD0Emq1YYkG7qHMDilCAmlqrA2BIKdqNXFGTDFMa8Qj1jcp0 # 4sihY/69Nz0J43LJ5f2g/uPGjVdN1PUYOo9oH5dtLnZ9cqf27iTjnlBa9VpX5AJS # u2JGCrzQwroCUH9qcEpUUGISqpuBXQLgcmzOef2uiX7XOGYh2I5lw7xSM1bdlWsF # hjqQ+IDbvpWAJ43RPghVQSm64bofra/kD4W2Bd866M/X42cuFHosv5R1kmPITNQ2 # xncibKCx+fjyQz8eSygifDXpLJ+0H/oCsngZ3omhgi+NzF+44byt4CXoKCkXsE+T # Py+i+if3aBVGx8nhzfwfvOLy1cvfEbDg0h/npkxfD82lKoEyilCssD6IcAVQXBXO # WZftTwssN/vXZYEcsTKtRt43ShGN7mHxe9Rwn3GD7pgbtPjgV3hoszxpJa7t1+8Y # he3o8loKnMoXVoFsz16cXabhM6eA9PRIZ2h81Nu/j7BmW81pGwK9FJ7/3XarvMqT # bX686nHwizZwC/NihzsBBX3HaW7Gjbt5eVB2gav2Ml49wSlO0/FkY1jikwYgGBmY # 0oKigetYx379NDw55Hk= # SIG # End signature block |