ConnectedKubernetes.Autorest/custom/Set-AzConnectedKubernetes.ps1
# ---------------------------------------------------------------------------------- # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # ---------------------------------------------------------------------------------- [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseSingularNouns', '', Justification = 'Kubernetes is a recognised term', Scope = 'Function', Target = 'Set-AzConnectedKubernetes')] [CmdletBinding()] param() <# .Synopsis API to set properties of the connected cluster resource .Description API to set properties of the connected cluster resource. Replaces all configuration of an existing connected cluster; any properties not specified will be reset to their default values. .Example Set-AzConnectedKubernetes -ClusterName azps_test_cluster -ResourceGroupName azps_test_group -Location eastus -GatewayResourceId $gatewayResourceId .Example Set-AzConnectedKubernetes -ClusterName azps_test_cluster1 -ResourceGroupName azps_test_group -Location eastus -KubeConfig $HOME\.kube\config -KubeContext azps_aks_t01 -DisableGateway .Outputs Microsoft.Azure.PowerShell.Cmdlets.ConnectedKubernetes.Models.Api20240715Preview.IConnectedCluster .Notes COMPLEX PARAMETER PROPERTIES To create the parameters described below, construct a hash table containing the appropriate properties. For information on hash tables, run Get-Help about_Hash_Tables. INPUTOBJECT <IConnectedCluster>: Location <String>: The geo-location where the resource lives AgentPublicKeyCertificate <String>: Base64 encoded public certificate used by the agent to do the initial handshake to the backend services in Azure. IdentityType <ResourceIdentityType>: The type of identity used for the connected cluster. The type 'SystemAssigned, includes a system created identity. The type 'None' means no identity is assigned to the connected cluster. [Tag <ITrackedResourceTags>]: Resource tags. [(Any) <String>]: This indicates any property can be added to this object. [AadProfileAdminGroupObjectID <String[]>]: The list of AAD group object IDs that will have admin role of the cluster. [AadProfileEnableAzureRbac <Boolean?>]: Whether to enable Azure RBAC for Kubernetes authorization. [AadProfileTenantId <String>]: The AAD tenant ID to use for authentication. If not specified, will use the tenant of the deployment subscription. [ArcAgentProfileAgentAutoUpgrade <AutoUpgradeOptions?>]: Indicates whether the Arc agents on the be upgraded automatically to the latest version. Defaults to Enabled. [ArcAgentProfileAgentError <IAgentError[]>]: List of arc agentry and system components errors on the cluster resource. [ArcAgentProfileDesiredAgentVersion <String>]: Version of the Arc agents to be installed on the cluster resource [ArcAgentProfileSystemComponent <ISystemComponent[]>]: List of system extensions that are installed on the cluster resource. [MajorVersion <Int32?>]: Major Version of the system extension that is currently installed on the cluster resource. [Type <String>]: Type of the system extension [UserSpecifiedVersion <String>]: Version of the system extension to be installed on the cluster resource. [ArcAgentryConfiguration <IArcAgentryConfigurations[]>]: Configuration settings for customizing the behavior of the connected cluster. [Feature <String>]: Specifies the name of the feature for the configuration setting. [ProtectedSetting <IArcAgentryConfigurationsProtectedSettings>]: The configuration settings for the feature that contain any sensitive or secret information. [(Any) <String>]: This indicates any property can be added to this object. [Setting <IArcAgentryConfigurationsSettings>]: The configuration settings for the feature that do not contain any sensitive or secret information. [(Any) <String>]: This indicates any property can be added to this object. [AzureHybridBenefit <AzureHybridBenefit?>]: Indicates whether Azure Hybrid Benefit is opted in [Distribution <String>]: The Kubernetes distribution running on this connected cluster. [DistributionVersion <String>]: The Kubernetes distribution version on this connected cluster. [GatewayEnabled <Boolean?>]: Indicates whether the gateway for arc router connectivity is enabled. [GatewayResourceId <String>]: The resource ID of the gateway used for the Arc router feature. [Infrastructure <String>]: The infrastructure on which the Kubernetes cluster represented by this connected cluster is running on. [Kind <ConnectedClusterKind?>]: The kind of connected cluster. [OidcIssuerProfileEnabled <Boolean?>]: Whether to enable oidc issuer for workload identity integration. [OidcIssuerProfileSelfHostedIssuerUrl <String>]: The issuer url for public cloud clusters - AKS, EKS, GKE - used for the workload identity feature. [PrivateLinkScopeResourceId <String>]: This is populated only if privateLinkState is enabled. The resource id of the private link scope this connected cluster is assigned to, if any. [PrivateLinkState <PrivateLinkState?>]: Property which describes the state of private link on a connected cluster resource. [ProvisioningState <ProvisioningState?>]: Provisioning state of the connected cluster resource. [SystemDataCreatedAt <DateTime?>]: The timestamp of resource creation (UTC). [SystemDataCreatedBy <String>]: The identity that created the resource. [SystemDataCreatedByType <CreatedByType?>]: The type of identity that created the resource. [SystemDataLastModifiedAt <DateTime?>]: The timestamp of resource modification (UTC). [SystemDataLastModifiedBy <String>]: The identity that last modified the resource. [SystemDataLastModifiedByType <LastModifiedByType?>]: The type of identity that last modified the resource. [WorkloadIdentityEnabled <Boolean?>]: Whether to enable or disable the workload identity Webhook .Link https://learn.microsoft.com/powershell/module/az.connectedkubernetes/set-azconnectedkubernetes #> function Set-AzConnectedKubernetes { [OutputType([Microsoft.Azure.PowerShell.Cmdlets.ConnectedKubernetes.Models.Api20240715Preview.IConnectedCluster])] [CmdletBinding(DefaultParameterSetName = 'SetExpanded', PositionalBinding = $false, SupportsShouldProcess, ConfirmImpact = 'Medium')] [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingConvertToSecureStringWithPlainText', '', Justification = 'Code published before this issue was identified')] param( [Parameter(ParameterSetName = 'SetExpanded', Mandatory)] [Parameter(ParameterSetName = 'SetExpandedDisableGateway', Mandatory)] [Parameter(ParameterSetName = 'SetExpandedEnableGateway', Mandatory)] [Alias('Name')] [Microsoft.Azure.PowerShell.Cmdlets.ConnectedKubernetes.Category('Path')] [System.String] # The name of the Kubernetes cluster on which get is called. ${ClusterName}, [Parameter(ParameterSetName = 'SetExpanded', Mandatory)] [Parameter(ParameterSetName = 'SetExpandedDisableGateway', Mandatory)] [Parameter(ParameterSetName = 'SetExpandedEnableGateway', Mandatory)] [Microsoft.Azure.PowerShell.Cmdlets.ConnectedKubernetes.Category('Path')] [System.String] # The name of the resource group. # The name is case insensitive. ${ResourceGroupName}, [Parameter(ParameterSetName = 'SetExpanded', Mandatory)] [Parameter(ParameterSetName = 'SetExpandedDisableGateway', Mandatory)] [Parameter(ParameterSetName = 'SetExpandedEnableGateway', Mandatory)] [Microsoft.Azure.PowerShell.Cmdlets.ConnectedKubernetes.Category('Body')] [System.String] # The geo-location where the resource lives ${Location}, [Parameter(ParameterSetName = 'Set', Mandatory, ValueFromPipeline)] [Parameter(ParameterSetName = 'SetDisableGateway', Mandatory, ValueFromPipeline)] [Parameter(ParameterSetName = 'SetEnableGateway', Mandatory, ValueFromPipeline)] [Microsoft.Azure.PowerShell.Cmdlets.ConnectedKubernetes.Category('Body')] [Microsoft.Azure.PowerShell.Cmdlets.ConnectedKubernetes.Models.Api20240715Preview.IConnectedCluster] ${InputObject}, [Parameter()] [Microsoft.Azure.PowerShell.Cmdlets.ConnectedKubernetes.Category('Path')] [Microsoft.Azure.PowerShell.Cmdlets.ConnectedKubernetes.Runtime.DefaultInfo(Script = '(Get-AzContext).Subscription.Id')] [System.String] # The ID of the target subscription. ${SubscriptionId}, [Parameter()] [Microsoft.Azure.PowerShell.Cmdlets.ConnectedKubernetes.Category('Path')] [System.Uri] # The http URI of the proxy server for the kubernetes cluster to use ${HttpProxy}, [Parameter()] [Microsoft.Azure.PowerShell.Cmdlets.ConnectedKubernetes.Category('Path')] [System.Uri] # The https URI of the proxy server for the kubernetes cluster to use ${HttpsProxy}, [Parameter()] [Microsoft.Azure.PowerShell.Cmdlets.ConnectedKubernetes.Category('Path')] [System.String] # The comma-separated list of hostnames that should be excluded from the proxy server for the kubernetes cluster to use ${NoProxy}, [Parameter()] [Microsoft.Azure.PowerShell.Cmdlets.ConnectedKubernetes.Category('Path')] [System.String] # The path to the certificate file for proxy or custom Certificate Authority. ${ProxyCert}, [Parameter()] [Microsoft.Azure.PowerShell.Cmdlets.ConnectedKubernetes.Category('Path')] [System.Management.Automation.SwitchParameter] # Flag to disable auto upgrade of arc agents. ${DisableAutoUpgrade}, [Parameter()] [Microsoft.Azure.PowerShell.Cmdlets.ConnectedKubernetes.Category('Path')] [System.String] # Override the default container log path to enable fluent-bit logging. ${ContainerLogPath}, [Parameter(HelpMessage = "Path to the kube config file")] [Microsoft.Azure.PowerShell.Cmdlets.ConnectedKubernetes.Category('Body')] [System.String] # Path to the kube config file ${KubeConfig}, [Parameter(HelpMessage = "Kubconfig context from current machine")] [Microsoft.Azure.PowerShell.Cmdlets.ConnectedKubernetes.Category('Body')] [System.String] # Kubconfig context from current machine ${KubeContext}, [Parameter()] [ArgumentCompleter([Microsoft.Azure.PowerShell.Cmdlets.ConnectedKubernetes.Support.AzureHybridBenefit])] [Microsoft.Azure.PowerShell.Cmdlets.ConnectedKubernetes.Category('Body')] [Microsoft.Azure.PowerShell.Cmdlets.ConnectedKubernetes.Support.AzureHybridBenefit] # Indicates whether Azure Hybrid Benefit is opted in ${AzureHybridBenefit}, [Parameter()] [Microsoft.Azure.PowerShell.Cmdlets.ConnectedKubernetes.Category('Body')] [System.String] # The Kubernetes distribution running on this connected cluster. ${Distribution}, [Parameter()] [Microsoft.Azure.PowerShell.Cmdlets.ConnectedKubernetes.Category('Body')] [System.String] # The Kubernetes distribution version on this connected cluster. ${DistributionVersion}, [Parameter()] [Microsoft.Azure.PowerShell.Cmdlets.ConnectedKubernetes.Category('Body')] [System.String] # The infrastructure on which the Kubernetes cluster represented by this connected cluster is running on. ${Infrastructure}, [Parameter()] [Microsoft.Azure.PowerShell.Cmdlets.ConnectedKubernetes.Category('Body')] [System.String] # The resource id of the private link scope this connected cluster is assigned to, if any. ${PrivateLinkScopeResourceId}, [Parameter()] [ArgumentCompleter([Microsoft.Azure.PowerShell.Cmdlets.ConnectedKubernetes.Support.PrivateLinkState])] [Microsoft.Azure.PowerShell.Cmdlets.ConnectedKubernetes.Category('Body')] [Microsoft.Azure.PowerShell.Cmdlets.ConnectedKubernetes.Support.PrivateLinkState] # Property which describes the state of private link on a connected cluster resource. ${PrivateLinkState}, [Parameter()] [ArgumentCompleter([Microsoft.Azure.PowerShell.Cmdlets.ConnectedKubernetes.Support.ProvisioningState])] [Microsoft.Azure.PowerShell.Cmdlets.ConnectedKubernetes.Category('Body')] [Microsoft.Azure.PowerShell.Cmdlets.ConnectedKubernetes.Support.ProvisioningState] # Provisioning state of the connected cluster resource. ${ProvisioningState}, [Parameter()] [Microsoft.Azure.PowerShell.Cmdlets.ConnectedKubernetes.Category('Body')] [Microsoft.Azure.PowerShell.Cmdlets.ConnectedKubernetes.Runtime.Info(PossibleTypes = ([Microsoft.Azure.PowerShell.Cmdlets.ConnectedKubernetes.Models.Api20.ITrackedResourceTags]))] [System.Collections.Hashtable] # Resource tags. ${Tag}, [Parameter()] [Microsoft.Azure.PowerShell.Cmdlets.ConnectedKubernetes.Category('Body')] [System.String] # OID of 'custom-locations' app. ${CustomLocationsOid}, [Parameter()] [Microsoft.Azure.PowerShell.Cmdlets.ConnectedKubernetes.Category('Body')] [System.Management.Automation.SwitchParameter] # Whether to enable oidc issuer for workload identity integration. ${OidcIssuerProfileEnabled}, [Parameter()] [Microsoft.Azure.PowerShell.Cmdlets.ConnectedKubernetes.Category('Body')] [System.String] # The issuer url for public cloud clusters - AKS, EKS, GKE - used for the workload identity feature. ${OidcIssuerProfileSelfHostedIssuerUrl}, [Parameter()] [Microsoft.Azure.PowerShell.Cmdlets.ConnectedKubernetes.Category('Body')] [System.Management.Automation.SwitchParameter] # Enable the workload identity Webhook ${WorkloadIdentityEnabled}, [Parameter()] [System.Management.Automation.SwitchParameter] # Accept EULA of ConnectedKubernetes, legal term will pop up without this parameter provided ${AcceptEULA}, [Parameter()] [Alias('AzureRMContext', 'AzureCredential')] [ValidateNotNull()] [Microsoft.Azure.PowerShell.Cmdlets.ConnectedKubernetes.Category('Azure')] [System.Management.Automation.PSObject] # The credentials, account, tenant, and subscription used for communication with Azure. ${DefaultProfile}, [Parameter()] [Microsoft.Azure.PowerShell.Cmdlets.ConnectedKubernetes.Category('Runtime')] [System.Management.Automation.SwitchParameter] # Run the command as a job ${AsJob}, [Parameter(DontShow)] [Microsoft.Azure.PowerShell.Cmdlets.ConnectedKubernetes.Category('Runtime')] [System.Management.Automation.SwitchParameter] # Wait for .NET debugger to attach ${Break}, [Parameter(DontShow)] [ValidateNotNull()] [Microsoft.Azure.PowerShell.Cmdlets.ConnectedKubernetes.Category('Runtime')] [Microsoft.Azure.PowerShell.Cmdlets.ConnectedKubernetes.Runtime.SendAsyncStep[]] # SendAsync Pipeline Steps to be appended to the front of the pipeline ${HttpPipelineAppend}, [Parameter(DontShow)] [ValidateNotNull()] [Microsoft.Azure.PowerShell.Cmdlets.ConnectedKubernetes.Category('Runtime')] [Microsoft.Azure.PowerShell.Cmdlets.ConnectedKubernetes.Runtime.SendAsyncStep[]] # SendAsync Pipeline Steps to be prepended to the front of the pipeline ${HttpPipelinePrepend}, [Parameter()] [Microsoft.Azure.PowerShell.Cmdlets.ConnectedKubernetes.Category('Runtime')] [System.Management.Automation.SwitchParameter] # Run the command asynchronously ${NoWait}, [Parameter(DontShow)] [Microsoft.Azure.PowerShell.Cmdlets.ConnectedKubernetes.Category('Runtime')] [System.Uri] # The URI of the proxy server for host os to use ${Proxy}, [Parameter(DontShow)] [ValidateNotNull()] [Microsoft.Azure.PowerShell.Cmdlets.ConnectedKubernetes.Category('Runtime')] [System.Management.Automation.PSCredential] # The credential of the proxy server for host os to use ${ProxyCredential}, [Parameter(DontShow)] [Microsoft.Azure.PowerShell.Cmdlets.ConnectedKubernetes.Category('Runtime')] [System.Management.Automation.SwitchParameter] # Use the default credentials for the proxy ${ProxyUseDefaultCredentials}, [Parameter()] [Microsoft.Azure.PowerShell.Cmdlets.ConnectedKubernetes.Category('Body')] [System.Collections.Hashtable] # Arc Agentry System Configuration ${ConfigurationSetting}, [Parameter()] [Microsoft.Azure.PowerShell.Cmdlets.ConnectedKubernetes.Category('Body')] [System.Collections.Hashtable] # Arc Agentry System Protected Configuration ${ConfigurationProtectedSetting}, [Parameter(ParameterSetName = 'SetDisableGateway', Mandatory)] [Parameter(ParameterSetName = 'SetExpandedDisableGateway', Mandatory)] [Microsoft.Azure.PowerShell.Cmdlets.ConnectedKubernetes.Category('body')] [System.Management.Automation.SwitchParameter] ${DisableGateway}, [Parameter(ParameterSetName = 'SetEnableGateway', Mandatory)] [Parameter(ParameterSetName = 'SetExpandedEnableGateway', Mandatory)] [Microsoft.Azure.PowerShell.Cmdlets.ConnectedKubernetes.Category('body')] [System.String] # Arc Gateway resource Id, providing this will enable the gateway ${GatewayResourceId} ) process { Write-Debug "Checking if Azure Hybrid Benefit is opted in and processing the EULA." . "$PSScriptRoot/helpers/HelmHelper.ps1" . "$PSScriptRoot/helpers/ConfigDPHelper.ps1" . "$PSScriptRoot/helpers/AzCloudMetadataHelper.ps1" . "$PSScriptRoot/helpers/UtilsHelper.ps1" # Configuration is structured as a hashtable of hashtables where the final # values must be strings. Check this! Test-ConfigurationSyntax -name 'ConfigurationSetting' Test-ConfigurationSyntax -configuration 'ConfigurationProtectedSetting' $ProtectedSettingsPlaceholderValue = "redacted" if ($AzureHybridBenefit) { if (!$AcceptEULA) { $legalTermPath = Join-Path $PSScriptRoot -ChildPath "LegalTerm.txt" try { $legalTerm = (Get-Content -Path $legalTermPath) -join "`r`n" } catch { Write-Error "Get legal term failed." throw } $confirmation = Read-Host $legalTerm"`n[Y] Yes [N] No (default is `"N`")" if ($confirmation -ine "Y") { Return } } } Write-Debug "Removed the AcceptEULA and InputObject parameters after processing." $null = $PSBoundParameters.Remove('AcceptEULA') $null = $PSBoundParameters.Remove('InputObject') Write-Debug "Determining the kube config file path." if ($PSBoundParameters.ContainsKey("KubeConfig")) { $Null = $PSBoundParameters.Remove('KubeConfig') } elseif (Test-Path Env:KUBECONFIG) { $KubeConfig = Get-ChildItem -Path $Env:KUBECONFIG } elseif (Test-Path Env:Home) { $KubeConfig = Join-Path -Path $Env:Home -ChildPath '.kube' | Join-Path -ChildPath 'config' } else { $KubeConfig = Join-Path -Path $Home -ChildPath '.kube' | Join-Path -ChildPath 'config' } Write-Debug "Setting the kube context." if (-not (Test-Path $KubeConfig)) { Write-Error 'Cannot find the kube-config. Please make sure that you have the kube-config on your machine.' return } if ($PSBoundParameters.ContainsKey('KubeContext')) { $Null = $PSBoundParameters.Remove('KubeContext') } if (($null -eq $KubeContext) -or ($KubeContext -eq '')) { $KubeContext = kubectl config current-context } # The internal Set command does not support inputObject probably due to current implementation of swagger # So we do it hard way and parse value from inputObject # ArcAgentryConfiguration is handled in a separate block if ($null -ne $InputObject) { $Location = $InputObject.Location $PSBoundParameters.Add('Location', $Location) $ClusterName = $InputObject.Name $PSBoundParameters.Add('ClusterName', $ClusterName) $ResourceGroupName = $InputObject.ResourceGroupName $PSBoundParameters.Add('ResourceGroupName', $ResourceGroupName) if (-not $PSBoundParameters.ContainsKey('DisableGateway') -and $InputObject.PSObject.Properties['GatewayEnabled']) { $DisableGateway = -not $InputObject.GatewayEnabled } if ((-not $PSBoundParameters.ContainsKey('GatewayResourceId')) -and (-not [String]::IsNullOrEmpty($InputObject.GatewayResourceId))) { $GatewayResourceId = $InputObject.GatewayResourceId } if (-not $PSBoundParameters.ContainsKey('DisableAutoUpgrade')) { $DisableAutoUpgrade = ($InputObject.ArcAgentProfileAgentAutoUpgrade -eq 'Disabled') } # Merge the fields that use a common merging process. Merge-MaybeNullInput -InputObject $InputObject -LclPSBoundParameters $PSBoundParameters } if ($PSBoundParameters.ContainsKey('GatewayResourceId')) { Write-Debug "Gateway enabled" $PSBoundParameters.Add('GatewayEnabled', $true) } elseif ($PSBoundParameters.ContainsKey('DisableGateway')) { Write-Debug "Gateway disabled" $Null = $PSBoundParameters.Remove('DisableGateway') $PSBoundParameters.Add('GatewayEnabled', $false) } else { $PSBoundParameters.Add('GatewayEnabled', -not $DisableGateway) if (-not [String]::IsNullOrEmpty($GatewayResourceId)) { $PSBoundParameters.Add('GatewayResourceId', $GatewayResourceId) } } $CommonPSBoundParameters = @{} if ($PSBoundParameters.ContainsKey('HttpPipelineAppend')) { $CommonPSBoundParameters['HttpPipelineAppend'] = $HttpPipelineAppend } if ($PSBoundParameters.ContainsKey('HttpPipelinePrepend')) { $CommonPSBoundParameters['HttpPipelinePrepend'] = $HttpPipelinePrepend } if ($PSBoundParameters.ContainsKey('SubscriptionId')) { $CommonPSBoundParameters['SubscriptionId'] = $SubscriptionId } if ($PSBoundParameters.ContainsKey('PrivateLinkState') -and ($null -ne $CustomLocationsOid) -and ($CustomLocationsOid -ne '')) { Write-Warning "The features 'cluster-connect' and 'custom-locations' cannot be enabled for a private link enabled connected cluster." $CustomLocationsOid = $null } if ($PSBoundParameters.ContainsKey('CustomLocationsOid')) { $Null = $PSBoundParameters.Remove('CustomLocationsOid') } $IdentityType = [Microsoft.Azure.PowerShell.Cmdlets.ConnectedKubernetes.Support.ResourceIdentityType]::SystemAssigned $PSBoundParameters.Add('IdentityType', $IdentityType) #Region check helm install Confirm-HelmVersion ` -KubeConfig $KubeConfig ` -Verbose:($PSCmdlet.MyInvocation.BoundParameters["Verbose"].IsPresent -eq $true) ` -Debug:($PSCmdlet.MyInvocation.BoundParameters["Debug"].IsPresent -eq $true) #EndRegion $helmClientLocation = 'helm' #Region get release namespace $ReleaseNamespaces = Get-HelmReleaseNamespace -KubeConfig $KubeConfig -KubeContext $KubeContext $ReleaseNamespace = $ReleaseNamespaces['ReleaseNamespace'] $ReleaseInstallNamespace = $ReleaseNamespaces['ReleaseInstallNamespace'] #Endregion #Region validate release namespace if (-not ([string]::IsNullOrEmpty($ReleaseNamespace))) { $Configmap = kubectl get configmap --namespace azure-arc azure-clusterconfig -o json --kubeconfig $KubeConfig | ConvertFrom-Json $ConfigmapRgName = $Configmap.data.AZURE_RESOURCE_GROUP $ConfigmapClusterName = $Configmap.data.AZURE_RESOURCE_NAME try { $ExistConnectedKubernetes = Get-AzConnectedKubernetes ` -ResourceGroupName $ConfigmapRgName ` -ClusterName $ConfigmapClusterName ` @CommonPSBoundParameters ` -ErrorAction 'silentlycontinue' $PSBoundParameters.Add('AgentPublicKeyCertificate', $ExistConnectedKubernetes.AgentPublicKeyCertificate) if (($ResourceGroupName.ToLower() -ne $ConfigmapRgName.ToLower()) -or ($ClusterName.ToLower() -ne $ConfigmapClusterName.ToLower())) { Write-Error "The provided cluster name and rg correspond to different cluster" return } } catch { Write-Error "The corresponding connected cluster resource does not exist" return } } else { Write-Error "The azure-arc release namespace couldn't be retrieved, which implies that the kubernetes cluster has not been onboarded to azure-arc." return } #Endregion # Adding Helm repo $RegistryPath = Set-HelmModulesAndRepository -KubeConfig $KubeConfig -KubeContext $KubeContext -Location $Location Write-Debug "Processing Helm chart installation options." $options = "" if ($DisableAutoUpgrade) { $Null = $PSBoundParameters.Remove('DisableAutoUpgrade') $PSBoundParameters.Add('ArcAgentProfileAgentAutoUpgrade', 'Disabled') } if (-not ([string]::IsNullOrEmpty($ContainerLogPath))) { $options += " --set systemDefaultValues.fluent-bit.containerLogPath=$ContainerLogPath" $Null = $PSBoundParameters.Remove('ContainerLogPath') } if (-not ([string]::IsNullOrEmpty($KubeConfig))) { $options += " --kubeconfig $KubeConfig" } if (-not ([string]::IsNullOrEmpty($KubeContext))) { $options += " --kube-context $KubeContext" } if (-not ([string]::IsNullOrEmpty($CustomLocationsOid))) { $options += " --set systemDefaultValues.customLocations.oid=$CustomLocationsOid" $options += " --set systemDefaultValues.customLocations.enabled=true" } if ((-not ([string]::IsNullOrEmpty($Proxy))) -and (-not $PSBoundParameters.ContainsKey('ProxyCredential'))) { if (-not ([string]::IsNullOrEmpty($Proxy.UserInfo))) { try { $userInfo = $Proxy.UserInfo -Split ':' $pass = ConvertTo-SecureString $userInfo[1] -AsPlainText -Force $ProxyCredential = New-Object System.Management.Automation.PSCredential ($userInfo[0] , $pass) $PSBoundParameters.Add('ProxyCredential', $ProxyCredential) } catch { throw "Please set ProxyCredential or provide username and password in the Proxy parameter" } } else { Write-Warning "If the proxy is a private proxy, pass ProxyCredential parameter or provide username and password in the Proxy parameter" } } #Endregion #Region Deal with configuration settings and protected settings if ($null -eq $ConfigurationSetting) { $ConfigurationSetting = @{} } if ($null -eq $ConfigurationProtectedSetting) { $ConfigurationProtectedSetting = @{} } if ($null -ne $InputObject) { foreach ($arcAgentConfig in $InputObject.ArcAgentryConfiguration) { $ConfigurationSetting[$arcAgentConfig.feature] = $arcAgentConfig.settings $ConfigurationProtectedSetting[$arcAgentConfig.feature] = $arcAgentConfig.protectedSettings } } if (-not $ConfigurationProtectedSetting.ContainsKey("proxy")) { $ConfigurationProtectedSetting["proxy"] = @{} } if (-not ([string]::IsNullOrEmpty($HttpProxy))) { $HttpProxyStr = $HttpProxy.ToString() $HttpProxyStr = $HttpProxyStr -replace ',', '\,' $HttpProxyStr = $HttpProxyStr -replace '/', '\/' $ConfigurationProtectedSetting["proxy"]["http_proxy"] = $HttpProxyStr # Note how we are removing k8s parameters from the list of parameters # to pass to the internal (creates ARM object) command. $Null = $PSBoundParameters.Remove('HttpProxy') } if (-not ([string]::IsNullOrEmpty($HttpsProxy))) { $HttpsProxyStr = $HttpsProxy.ToString() $HttpsProxyStr = $HttpsProxyStr -replace ',', '\,' $HttpsProxyStr = $HttpsProxyStr -replace '/', '\/' $ConfigurationProtectedSetting["proxy"]["https_proxy"] = $HttpsProxyStr $Null = $PSBoundParameters.Remove('HttpsProxy') } if (-not ([string]::IsNullOrEmpty($NoProxy))) { $NoProxy = $NoProxy -replace ',', '\,' $NoProxy = $NoProxy -replace '/', '\/' $ConfigurationProtectedSetting["proxy"]["no_proxy"] = $NoProxy $Null = $PSBoundParameters.Remove('NoProxy') } try { if ((-not ([string]::IsNullOrEmpty($ProxyCert))) -and (Test-Path $ProxyCert)) { $ConfigurationProtectedSetting["proxy"]["proxy_cert"] = $ProxyCert } } catch { throw "Unable to find ProxyCert from file path" } $RedactedProtectedConfiguration = @{} # Duplicate the protected settings into the settings. foreach ($feature in $ConfigurationProtectedSetting.Keys) { if (-not $RedactedProtectedConfiguration.ContainsKey($feature)) { $RedactedProtectedConfiguration[$feature] = @{} } foreach ($setting in $ConfigurationProtectedSetting[$feature].Keys) { $RedactedProtectedConfiguration[$feature][$setting] = "${ProtectedSettingsPlaceholderValue}:${feature}:${setting}" } } #Endregion # A lot of what follows relies on knowing the cloud we are using and the # various endpoints so get that information now. $cloudMetadata = Get-AzCloudMetadata ` -Verbose:($PSCmdlet.MyInvocation.BoundParameters["Verbose"].IsPresent -eq $true) ` -Debug:($PSCmdlet.MyInvocation.BoundParameters["Debug"].IsPresent -eq $true) # Perform DP health check $configDpinfo = Get-ConfigDPEndpoint ` -location $Location ` -Cloud $cloudMetadata ` -Verbose:($PSCmdlet.MyInvocation.BoundParameters["Verbose"].IsPresent -eq $true) ` -Debug:($PSCmdlet.MyInvocation.BoundParameters["Debug"].IsPresent -eq $true) $configDPEndpoint = $configDpInfo.configDPEndpoint # If the health check fails (not 200 response), an exception is thrown # so we can ignore the output. $null = Invoke-ConfigDPHealthCheck ` -configDPEndpoint $configDPEndpoint ` -Verbose:($PSCmdlet.MyInvocation.BoundParameters["Verbose"].IsPresent -eq $true) ` -Debug:($PSCmdlet.MyInvocation.BoundParameters["Debug"].IsPresent -eq $true) # This call does the "pure ARM" update of the ARM objects. Write-Debug "Updating Connected Kubernetes ARM objects." # Process the Arc agentry settings and protected settings # Create any empty array of IArcAgentryConfigurations. # shortened name to avoid class with type name. # # **NOTE** The Swagger naming does NOT match the names that will be used # in the final helm values file. Instead there needs to be an # explicit mapping which is done in TWO places: # 1. The ConfigDP is able to map the (unprotected) settings but # does not have access to the protected settings so... # 2. This Powershell script has to perform the mapping for # protected settings. # # This DOES mean that code changes are required both in the # Config DP annd this Powershell script if a new Kubernetes # feature is added. # Do not send protected settings to CCRP $arcAgentryConfigs = ConvertTo-ArcAgentryConfiguration ` -ConfigurationSetting $ConfigurationSetting ` -RedactedProtectedConfiguration @{} ` -CCRP $true ` -Verbose:($PSCmdlet.MyInvocation.BoundParameters["Verbose"].IsPresent -eq $true) ` -Debug:($PSCmdlet.MyInvocation.BoundParameters["Debug"].IsPresent -eq $true) # It is possible to set an empty value for these parameters and then # the code above gets skipped but we still need to remove the empty # values from $PSBoundParameters. if ($PSBoundParameters.ContainsKey('ConfigurationSetting')) { $PSBoundParameters.Remove('ConfigurationSetting') } if ($PSBoundParameters.ContainsKey('ConfigurationProtectedSetting')) { $PSBoundParameters.Remove('ConfigurationProtectedSetting') } $PSBoundParameters.Add('ArcAgentryConfiguration', $arcAgentryConfigs) Write-Verbose "Updating the connected cluster resource...." $CCResponse = Az.ConnectedKubernetes.internal\Set-AzConnectedKubernetes @PSBoundParameters if ((-not $WhatIfPreference) -and (-not $CCResponse)) { Write-Error "Failed to update the 'Kubernetes - Azure Arc' resource" return } # Wait for the agent state to settle before proceeding. If it doesn't, # we'll continue anyway - but remember and throw an error at the end. $agentsInTerminalState = $true if ($PSCmdlet.ShouldProcess($ClusterName, "Check agent state of the connected cluster")) { $timeout = [datetime]::Now.AddMinutes(60) for (;;) { $CCResponse = Get-AzConnectedKubernetes -ResourceGroupName $ResourceGroupName -ClusterName $ClusterName @CommonPSBoundParameters if ($null -eq $CCResponse.ArcAgentProfileAgentState) { Write-Verbose "No agent configuration in progress." break } if ($CCResponse.ArcAgentProfileAgentState -eq "Succeeded") { Write-Verbose "Cluster agent configuration succeeded." break } if ($CCResponse.ArcAgentProfileAgentState -eq "Failed") { Write-Error "Cluster agent configuration failed." break } if ([datetime]::Now -ge $timeout) { Write-Error "Cluster agent configuration timed out after 60 minutes." $agentsInTerminalState = $false break } Write-Verbose "Cluster agent configuration is in progress..." Start-Sleep -Seconds 30 } } $arcAgentryConfigs = ConvertTo-ArcAgentryConfiguration ` -ConfigurationSetting $ConfigurationSetting ` -RedactedProtectedConfiguration $RedactedProtectedConfiguration ` -CCRP $false ` -Verbose:($PSCmdlet.MyInvocation.BoundParameters["Verbose"].IsPresent -eq $true) ` -Debug:($PSCmdlet.MyInvocation.BoundParameters["Debug"].IsPresent -eq $true) # Convert the $Response object into a nested hashtable. Write-Debug "PUT response: $CCResponse" $Response = ConvertFrom-Json "$CCResponse" $Response = ConvertTo-Hashtable $Response # Whatif may return empty response if (-not $Response) { $Response = @{} } if (-not $Response.ContainsKey('properties')) { $Response['properties'] = @{} } $Response['properties']['arcAgentryConfigurations'] = $arcAgentryConfigs # Retrieving Helm chart OCI (Open Container Initiative) Artifact location Write-Debug "Retrieving Helm chart OCI (Open Container Initiative) Artifact location." $ResponseStr = $Response | ConvertTo-Json -Depth 10 Write-Debug "PUT response: $ResponseStr" Write-Verbose "Preparing helm ...." if ($PSCmdlet.ShouldProcess('configDP', 'get helm values from config DP')) { $helmValuesDp = Get-HelmValuesFromConfigDP ` -configDPEndpoint $configDPEndpoint ` -releaseTrain $ReleaseTrain ` -requestBody $ResponseStr ` -Verbose:($PSCmdlet.MyInvocation.BoundParameters["Verbose"].IsPresent -eq $true) ` -Debug:($PSCmdlet.MyInvocation.BoundParameters["Debug"].IsPresent -eq $true) Write-Debug "helmValuesDp: $helmValuesDp" Write-Debug "OCI Artifact location: ${helmValuesDp.repositoryPath}." $registryPath = if ($env:HELMREGISTRY) { $env:HELMREGISTRY } else { $helmValuesDp.repositoryPath } Write-Debug "RegistryPath: ${registryPath}." $helmValuesContent = $helmValuesDp.helmValuesContent Write-Debug "Helm values: ${helmValuesContent}." $optionsFromDp = "" foreach ($field in $helmValuesContent.PSObject.Properties) { if ($field.Value.StartsWith($ProtectedSettingsPlaceholderValue)) { $parsedValue = $field.Value.Split(":") # "${ProtectedSettingsPlaceholderValue}:${feature}:${setting}" $field.Value = $ConfigurationProtectedSetting[$parsedValue[1]][$parsedValue[2]] } if ($field.Name -eq "global.proxyCert") { $optionsFromDp += " --set-file $($field.Name)=$($field.Value)" } $optionsFromDp += " --set $($field.Name)=$($field.Value)" } # In helm, priority is given to new values, so we append $options contains user input last $options = $optionsFromDp + $options # Set agent version in registry path if ($ExistConnectedKubernetes.AgentVersion) { $repositoryPath = $repositoryPath -replace "(?<=:).*", $ExistConnectedKubernetes.AgentVersion } } if ($PSCmdlet.ShouldProcess('configDP', 'get helm chart path')) { # Get helm chart path (within the OCI registry). $chartPath = Get-HelmChartPath ` -registryPath $registryPath ` -kubeConfig $KubeConfig ` -kubeContext $KubeContext ` -helmClientLocation $HelmClientLocation ` -Verbose:($PSCmdlet.MyInvocation.BoundParameters["Verbose"].IsPresent -eq $true) ` -Debug:($PSCmdlet.MyInvocation.BoundParameters["Debug"].IsPresent -eq $true) if (Test-Path Env:HELMCHART) { $ChartPath = Get-ChildItem -Path $Env:HELMCHART } } # Get current helm values if ($PSCmdlet.ShouldProcess($ClusterName, "Get current helm values")) { $userValuesLocation = Get-HelmValue ` -HelmClientLocation $HelmClientLocation ` -Namespace $ReleaseInstallNamespace ` -KubeConfig $KubeConfig ` -KubeContext $KubeContext ` -Verbose:($PSCmdlet.MyInvocation.BoundParameters["Verbose"].IsPresent -eq $true) ` -Debug:($PSCmdlet.MyInvocation.BoundParameters["Debug"].IsPresent -eq $true) } Write-Verbose "Executing helm upgrade, this can take a few minutes ...." Write-Debug $options -ErrorAction Continue if ($DebugPreference -eq "Continue") { $options += " --debug" } if ($PSCmdlet.ShouldProcess($ClusterName, "Update Kubernetes cluster with Azure Arc")) { try { helm upgrade ` azure-arc ` $ChartPath ` --namespace $ReleaseInstallNamespace ` -f $userValuesLocation ` --wait (-split $options) | Out-Null } catch { throw "Unable to install helm release" } } # If there was a problem with agent state, throw the error now. if ($agentsInTerminalState -eq $false) { throw "Timed out waiting for Agent State to reach terminal state." } Return $CCResponse } } function Merge-MaybeNullInput { [Microsoft.Azure.PowerShell.Cmdlets.ConnectedKubernetes.DoNotExportAttribute()] param( [Microsoft.Azure.PowerShell.Cmdlets.ConnectedKubernetes.Models.Api20240715Preview.IConnectedCluster] $InputObject, [System.Collections.Generic.Dictionary[system.String, System.Object]] $LclPSBoundParameters ) $mergeFields = 'WorkloadIdentityEnabled', 'OidcIssuerProfileEnabled', 'OidcIssuerProfileSelfHostedIssuerUrl', 'Distribution', 'DistributionVersion', 'Infrastructure', 'PrivateLinkState' foreach ($mergeField in $mergeFields) { if ((-not $LclPSBoundParameters.ContainsKey($mergeField)) -and $InputObject.PSObject.Properties[$mergeField] -and $null -ne $InputObject.PSObject.Properties[$mergeField].Value) { $parameterValue = $InputObject.PSObject.Properties[$mergeField].Value $LclPSBoundParameters.Add($mergeField, $parameterValue) } } } # SIG # Begin signature block # MIIoOQYJKoZIhvcNAQcCoIIoKjCCKCYCAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCCrsQchJEbxyqXU # XKVGzL33RNw0nMxLo0cFgjg0dSFKIKCCDYUwggYDMIID66ADAgECAhMzAAAEA73V # lV0POxitAAAAAAQDMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD # VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p # bmcgUENBIDIwMTEwHhcNMjQwOTEyMjAxMTEzWhcNMjUwOTExMjAxMTEzWjB0MQsw # CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u # ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB # AQCfdGddwIOnbRYUyg03O3iz19XXZPmuhEmW/5uyEN+8mgxl+HJGeLGBR8YButGV # LVK38RxcVcPYyFGQXcKcxgih4w4y4zJi3GvawLYHlsNExQwz+v0jgY/aejBS2EJY # oUhLVE+UzRihV8ooxoftsmKLb2xb7BoFS6UAo3Zz4afnOdqI7FGoi7g4vx/0MIdi # kwTn5N56TdIv3mwfkZCFmrsKpN0zR8HD8WYsvH3xKkG7u/xdqmhPPqMmnI2jOFw/ # /n2aL8W7i1Pasja8PnRXH/QaVH0M1nanL+LI9TsMb/enWfXOW65Gne5cqMN9Uofv # ENtdwwEmJ3bZrcI9u4LZAkujAgMBAAGjggGCMIIBfjAfBgNVHSUEGDAWBgorBgEE # AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQU6m4qAkpz4641iK2irF8eWsSBcBkw # VAYDVR0RBE0wS6RJMEcxLTArBgNVBAsTJE1pY3Jvc29mdCBJcmVsYW5kIE9wZXJh # dGlvbnMgTGltaXRlZDEWMBQGA1UEBRMNMjMwMDEyKzUwMjkyNjAfBgNVHSMEGDAW # gBRIbmTlUAXTgqoXNzcitW2oynUClTBUBgNVHR8ETTBLMEmgR6BFhkNodHRwOi8v # d3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NybC9NaWNDb2RTaWdQQ0EyMDExXzIw # MTEtMDctMDguY3JsMGEGCCsGAQUFBwEBBFUwUzBRBggrBgEFBQcwAoZFaHR0cDov # L3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9jZXJ0cy9NaWNDb2RTaWdQQ0EyMDEx # XzIwMTEtMDctMDguY3J0MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggIB # AFFo/6E4LX51IqFuoKvUsi80QytGI5ASQ9zsPpBa0z78hutiJd6w154JkcIx/f7r # EBK4NhD4DIFNfRiVdI7EacEs7OAS6QHF7Nt+eFRNOTtgHb9PExRy4EI/jnMwzQJV # NokTxu2WgHr/fBsWs6G9AcIgvHjWNN3qRSrhsgEdqHc0bRDUf8UILAdEZOMBvKLC # rmf+kJPEvPldgK7hFO/L9kmcVe67BnKejDKO73Sa56AJOhM7CkeATrJFxO9GLXos # oKvrwBvynxAg18W+pagTAkJefzneuWSmniTurPCUE2JnvW7DalvONDOtG01sIVAB # +ahO2wcUPa2Zm9AiDVBWTMz9XUoKMcvngi2oqbsDLhbK+pYrRUgRpNt0y1sxZsXO # raGRF8lM2cWvtEkV5UL+TQM1ppv5unDHkW8JS+QnfPbB8dZVRyRmMQ4aY/tx5x5+ # sX6semJ//FbiclSMxSI+zINu1jYerdUwuCi+P6p7SmQmClhDM+6Q+btE2FtpsU0W # +r6RdYFf/P+nK6j2otl9Nvr3tWLu+WXmz8MGM+18ynJ+lYbSmFWcAj7SYziAfT0s # IwlQRFkyC71tsIZUhBHtxPliGUu362lIO0Lpe0DOrg8lspnEWOkHnCT5JEnWCbzu # iVt8RX1IV07uIveNZuOBWLVCzWJjEGa+HhaEtavjy6i7MIIHejCCBWKgAwIBAgIK # YQ6Q0gAAAAAAAzANBgkqhkiG9w0BAQsFADCBiDELMAkGA1UEBhMCVVMxEzARBgNV # BAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jv # c29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJvb3QgQ2VydGlm # aWNhdGUgQXV0aG9yaXR5IDIwMTEwHhcNMTEwNzA4MjA1OTA5WhcNMjYwNzA4MjEw # OTA5WjB+MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UE # BxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSgwJgYD # VQQDEx9NaWNyb3NvZnQgQ29kZSBTaWduaW5nIFBDQSAyMDExMIICIjANBgkqhkiG # 9w0BAQEFAAOCAg8AMIICCgKCAgEAq/D6chAcLq3YbqqCEE00uvK2WCGfQhsqa+la # UKq4BjgaBEm6f8MMHt03a8YS2AvwOMKZBrDIOdUBFDFC04kNeWSHfpRgJGyvnkmc # 6Whe0t+bU7IKLMOv2akrrnoJr9eWWcpgGgXpZnboMlImEi/nqwhQz7NEt13YxC4D # dato88tt8zpcoRb0RrrgOGSsbmQ1eKagYw8t00CT+OPeBw3VXHmlSSnnDb6gE3e+ # lD3v++MrWhAfTVYoonpy4BI6t0le2O3tQ5GD2Xuye4Yb2T6xjF3oiU+EGvKhL1nk # kDstrjNYxbc+/jLTswM9sbKvkjh+0p2ALPVOVpEhNSXDOW5kf1O6nA+tGSOEy/S6 # A4aN91/w0FK/jJSHvMAhdCVfGCi2zCcoOCWYOUo2z3yxkq4cI6epZuxhH2rhKEmd # X4jiJV3TIUs+UsS1Vz8kA/DRelsv1SPjcF0PUUZ3s/gA4bysAoJf28AVs70b1FVL # 5zmhD+kjSbwYuER8ReTBw3J64HLnJN+/RpnF78IcV9uDjexNSTCnq47f7Fufr/zd # sGbiwZeBe+3W7UvnSSmnEyimp31ngOaKYnhfsi+E11ecXL93KCjx7W3DKI8sj0A3 # T8HhhUSJxAlMxdSlQy90lfdu+HggWCwTXWCVmj5PM4TasIgX3p5O9JawvEagbJjS # 4NaIjAsCAwEAAaOCAe0wggHpMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBRI # bmTlUAXTgqoXNzcitW2oynUClTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTAL # BgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBRyLToCMZBD # uRQFTuHqp8cx0SOJNDBaBgNVHR8EUzBRME+gTaBLhklodHRwOi8vY3JsLm1pY3Jv # c29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFf # MDNfMjIuY3JsMF4GCCsGAQUFBwEBBFIwUDBOBggrBgEFBQcwAoZCaHR0cDovL3d3 # dy5taWNyb3NvZnQuY29tL3BraS9jZXJ0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFf # MDNfMjIuY3J0MIGfBgNVHSAEgZcwgZQwgZEGCSsGAQQBgjcuAzCBgzA/BggrBgEF # BQcCARYzaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9kb2NzL3ByaW1h # cnljcHMuaHRtMEAGCCsGAQUFBwICMDQeMiAdAEwAZQBnAGEAbABfAHAAbwBsAGkA # YwB5AF8AcwB0AGEAdABlAG0AZQBuAHQALiAdMA0GCSqGSIb3DQEBCwUAA4ICAQBn # 8oalmOBUeRou09h0ZyKbC5YR4WOSmUKWfdJ5DJDBZV8uLD74w3LRbYP+vj/oCso7 # v0epo/Np22O/IjWll11lhJB9i0ZQVdgMknzSGksc8zxCi1LQsP1r4z4HLimb5j0b # pdS1HXeUOeLpZMlEPXh6I/MTfaaQdION9MsmAkYqwooQu6SpBQyb7Wj6aC6VoCo/ # KmtYSWMfCWluWpiW5IP0wI/zRive/DvQvTXvbiWu5a8n7dDd8w6vmSiXmE0OPQvy # CInWH8MyGOLwxS3OW560STkKxgrCxq2u5bLZ2xWIUUVYODJxJxp/sfQn+N4sOiBp # mLJZiWhub6e3dMNABQamASooPoI/E01mC8CzTfXhj38cbxV9Rad25UAqZaPDXVJi # hsMdYzaXht/a8/jyFqGaJ+HNpZfQ7l1jQeNbB5yHPgZ3BtEGsXUfFL5hYbXw3MYb # BL7fQccOKO7eZS/sl/ahXJbYANahRr1Z85elCUtIEJmAH9AAKcWxm6U/RXceNcbS # oqKfenoi+kiVH6v7RyOA9Z74v2u3S5fi63V4GuzqN5l5GEv/1rMjaHXmr/r8i+sL # gOppO6/8MO0ETI7f33VtY5E90Z1WTk+/gFcioXgRMiF670EKsT/7qMykXcGhiJtX # cVZOSEXAQsmbdlsKgEhr/Xmfwb1tbWrJUnMTDXpQzTGCGgowghoGAgEBMIGVMH4x # CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRt # b25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01p # Y3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMTECEzMAAAQDvdWVXQ87GK0AAAAA # BAMwDQYJYIZIAWUDBAIBBQCgga4wGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQw # HAYKKwYBBAGCNwIBCzEOMAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIHkU # LL8xceDSDgWH3BujzJgOeOpjvBBLuSSrzxUANvRbMEIGCisGAQQBgjcCAQwxNDAy # oBSAEgBNAGkAYwByAG8AcwBvAGYAdKEagBhodHRwOi8vd3d3Lm1pY3Jvc29mdC5j # b20wDQYJKoZIhvcNAQEBBQAEggEAdtgJ7DGjHXk9y8JMPnHsMg3SUSH4Y+nE6NCk # qDep1Fxp135BNRcQrosnTJsPdwAtF7oePI0L0f8s08e+TAK4v6QmceYfU0i4P68j # Mm9acduF3Qk0kDmc4x5eeojtLdYx0n9bClke9ugBIb9ytfETEThkJuK7jFRsiqjt # YzslFU+5WOOfMu/rRhM2VQh4feOU3slKsVg6OyPSrQJm0jHNCC7/LulCHcEgvlRg # BiD1m8ZTLscTIsB0qrezE6UzJ9yn3Q9PGWd4c7fM+5TFsXpy9hhOSLh7EaiIlZBI # iC74mQ9DNL7QJaVPsGtNhXLUqvBzwIp24UUvHedr+YaIInlXn6GCF5QwgheQBgor # BgEEAYI3AwMBMYIXgDCCF3wGCSqGSIb3DQEHAqCCF20wghdpAgEDMQ8wDQYJYIZI # AWUDBAIBBQAwggFSBgsqhkiG9w0BCRABBKCCAUEEggE9MIIBOQIBAQYKKwYBBAGE # WQoDATAxMA0GCWCGSAFlAwQCAQUABCC58+n5A0f6ZWzyTrgZExrZDHnmncJc+zdk # JL43IobOAwIGZxp7vI6RGBMyMDI0MTExNDA5NDYxNC45MjFaMASAAgH0oIHRpIHO # MIHLMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH # UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSUwIwYDVQQL # ExxNaWNyb3NvZnQgQW1lcmljYSBPcGVyYXRpb25zMScwJQYDVQQLEx5uU2hpZWxk # IFRTUyBFU046QTAwMC0wNUUwLUQ5NDcxJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1l # LVN0YW1wIFNlcnZpY2WgghHqMIIHIDCCBQigAwIBAgITMwAAAevgGGy1tu847QAB # AAAB6zANBgkqhkiG9w0BAQsFADB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2Fz # aGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENv # cnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAx # MDAeFw0yMzEyMDYxODQ1MzRaFw0yNTAzMDUxODQ1MzRaMIHLMQswCQYDVQQGEwJV # UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UE # ChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSUwIwYDVQQLExxNaWNyb3NvZnQgQW1l # cmljYSBPcGVyYXRpb25zMScwJQYDVQQLEx5uU2hpZWxkIFRTUyBFU046QTAwMC0w # NUUwLUQ5NDcxJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2Uw # ggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDBFWgh2lbgV3eJp01oqiaF # BuYbNc7hSKmktvJ15NrB/DBboUow8WPOTPxbn7gcmIOGmwJkd+TyFx7KOnzrxnoB # 3huvv91fZuUugIsKTnAvg2BU/nfN7Zzn9Kk1mpuJ27S6xUDH4odFiX51ICcKl6EG # 4cxKgcDAinihT8xroJWVATL7p8bbfnwsc1pihZmcvIuYGnb1TY9tnpdChWr9EARu # Co3TiRGjM2Lp4piT2lD5hnd3VaGTepNqyakpkCGV0+cK8Vu/HkIZdvy+z5EL3ojT # dFLL5vJ9IAogWf3XAu3d7SpFaaoeix0e1q55AD94ZwDP+izqLadsBR3tzjq2RfrC # NL+Tmi/jalRto/J6bh4fPhHETnDC78T1yfXUQdGtmJ/utI/ANxi7HV8gAPzid9TY # jMPbYqG8y5xz+gI/SFyj+aKtHHWmKzEXPttXzAcexJ1EH7wbuiVk3sErPK9MLg1X # b6hM5HIWA0jEAZhKEyd5hH2XMibzakbp2s2EJQWasQc4DMaF1EsQ1CzgClDYIYG6 # rUhudfI7k8L9KKCEufRbK5ldRYNAqddr/ySJfuZv3PS3+vtD6X6q1H4UOmjDKdjo # W3qs7JRMZmH9fkFkMzb6YSzr6eX1LoYm3PrO1Jea43SYzlB3Tz84OvuVSV7NcidV # tNqiZeWWpVjfavR+Jj/JOQIDAQABo4IBSTCCAUUwHQYDVR0OBBYEFHSeBazWVcxu # 4qT9O5jT2B+qAerhMB8GA1UdIwQYMBaAFJ+nFV0AXmJdg/Tl0mWnG1M1GelyMF8G # A1UdHwRYMFYwVKBSoFCGTmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMv # Y3JsL01pY3Jvc29mdCUyMFRpbWUtU3RhbXAlMjBQQ0ElMjAyMDEwKDEpLmNybDBs # BggrBgEFBQcBAQRgMF4wXAYIKwYBBQUHMAKGUGh0dHA6Ly93d3cubWljcm9zb2Z0 # LmNvbS9wa2lvcHMvY2VydHMvTWljcm9zb2Z0JTIwVGltZS1TdGFtcCUyMFBDQSUy # MDIwMTAoMSkuY3J0MAwGA1UdEwEB/wQCMAAwFgYDVR0lAQH/BAwwCgYIKwYBBQUH # AwgwDgYDVR0PAQH/BAQDAgeAMA0GCSqGSIb3DQEBCwUAA4ICAQCDdN8voPd8C+VW # ZP3+W87c/QbdbWK0sOt9Z4kEOWng7Kmh+WD2LnPJTJKIEaxniOct9wMgJ8yQywR8 # WHgDOvbwqdqsLUaM4NrertI6FI9rhjheaKxNNnBZzHZLDwlkL9vCEDe9Rc0dGSVd # 5Bg3CWknV3uvVau14F55ESTWIBNaQS9Cpo2Opz3cRgAYVfaLFGbArNcRvSWvSUbe # I2IDqRxC4xBbRiNQ+1qHXDCPn0hGsXfL+ynDZncCfszNrlgZT24XghvTzYMHcXio # LVYo/2Hkyow6dI7uULJbKxLX8wHhsiwriXIDCnjLVsG0E5bR82QgcseEhxbU2d1R # VHcQtkUE7W9zxZqZ6/jPmaojZgXQO33XjxOHYYVa/BXcIuu8SMzPjjAAbujwTawp # azLBv997LRB0ZObNckJYyQQpETSflN36jW+z7R/nGyJqRZ3HtZ1lXW1f6zECAeP+ # 9dy6nmcCrVcOqbQHX7Zr8WPcghHJAADlm5ExPh5xi1tNRk+i6F2a9SpTeQnZXP50 # w+JoTxISQq7vBij2nitAsSLaVeMqoPi+NXlTUNZ2NdtbFr6Iir9ZK9ufaz3FxfvD # Zo365vLOozmQOe/Z+pu4vY5zPmtNiVIcQnFy7JZOiZVDI5bIdwQRai2quHKJ6ltU # dsi3HjNnieuE72fT4eWhxtmnN5HYCDCCB3EwggVZoAMCAQICEzMAAAAVxedrngKb # SZkAAAAAABUwDQYJKoZIhvcNAQELBQAwgYgxCzAJBgNVBAYTAlVTMRMwEQYDVQQI # EwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3Nv # ZnQgQ29ycG9yYXRpb24xMjAwBgNVBAMTKU1pY3Jvc29mdCBSb290IENlcnRpZmlj # YXRlIEF1dGhvcml0eSAyMDEwMB4XDTIxMDkzMDE4MjIyNVoXDTMwMDkzMDE4MzIy # NVowfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcT # B1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UE # AxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTAwggIiMA0GCSqGSIb3DQEB # AQUAA4ICDwAwggIKAoICAQDk4aZM57RyIQt5osvXJHm9DtWC0/3unAcH0qlsTnXI # yjVX9gF/bErg4r25PhdgM/9cT8dm95VTcVrifkpa/rg2Z4VGIwy1jRPPdzLAEBjo # YH1qUoNEt6aORmsHFPPFdvWGUNzBRMhxXFExN6AKOG6N7dcP2CZTfDlhAnrEqv1y # aa8dq6z2Nr41JmTamDu6GnszrYBbfowQHJ1S/rboYiXcag/PXfT+jlPP1uyFVk3v # 3byNpOORj7I5LFGc6XBpDco2LXCOMcg1KL3jtIckw+DJj361VI/c+gVVmG1oO5pG # ve2krnopN6zL64NF50ZuyjLVwIYwXE8s4mKyzbnijYjklqwBSru+cakXW2dg3viS # kR4dPf0gz3N9QZpGdc3EXzTdEonW/aUgfX782Z5F37ZyL9t9X4C626p+Nuw2TPYr # bqgSUei/BQOj0XOmTTd0lBw0gg/wEPK3Rxjtp+iZfD9M269ewvPV2HM9Q07BMzlM # jgK8QmguEOqEUUbi0b1qGFphAXPKZ6Je1yh2AuIzGHLXpyDwwvoSCtdjbwzJNmSL # W6CmgyFdXzB0kZSU2LlQ+QuJYfM2BjUYhEfb3BvR/bLUHMVr9lxSUV0S2yW6r1AF # emzFER1y7435UsSFF5PAPBXbGjfHCBUYP3irRbb1Hode2o+eFnJpxq57t7c+auIu # rQIDAQABo4IB3TCCAdkwEgYJKwYBBAGCNxUBBAUCAwEAATAjBgkrBgEEAYI3FQIE # FgQUKqdS/mTEmr6CkTxGNSnPEP8vBO4wHQYDVR0OBBYEFJ+nFV0AXmJdg/Tl0mWn # G1M1GelyMFwGA1UdIARVMFMwUQYMKwYBBAGCN0yDfQEBMEEwPwYIKwYBBQUHAgEW # M2h0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvRG9jcy9SZXBvc2l0b3J5 # Lmh0bTATBgNVHSUEDDAKBggrBgEFBQcDCDAZBgkrBgEEAYI3FAIEDB4KAFMAdQBi # AEMAQTALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBTV # 9lbLj+iiXGJo0T2UkFvXzpoYxDBWBgNVHR8ETzBNMEugSaBHhkVodHRwOi8vY3Js # Lm1pY3Jvc29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXRfMjAx # MC0wNi0yMy5jcmwwWgYIKwYBBQUHAQEETjBMMEoGCCsGAQUFBzAChj5odHRwOi8v # d3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dF8yMDEwLTA2 # LTIzLmNydDANBgkqhkiG9w0BAQsFAAOCAgEAnVV9/Cqt4SwfZwExJFvhnnJL/Klv # 6lwUtj5OR2R4sQaTlz0xM7U518JxNj/aZGx80HU5bbsPMeTCj/ts0aGUGCLu6WZn # OlNN3Zi6th542DYunKmCVgADsAW+iehp4LoJ7nvfam++Kctu2D9IdQHZGN5tggz1 # bSNU5HhTdSRXud2f8449xvNo32X2pFaq95W2KFUn0CS9QKC/GbYSEhFdPSfgQJY4 # rPf5KYnDvBewVIVCs/wMnosZiefwC2qBwoEZQhlSdYo2wh3DYXMuLGt7bj8sCXgU # 6ZGyqVvfSaN0DLzskYDSPeZKPmY7T7uG+jIa2Zb0j/aRAfbOxnT99kxybxCrdTDF # NLB62FD+CljdQDzHVG2dY3RILLFORy3BFARxv2T5JL5zbcqOCb2zAVdJVGTZc9d/ # HltEAY5aGZFrDZ+kKNxnGSgkujhLmm77IVRrakURR6nxt67I6IleT53S0Ex2tVdU # CbFpAUR+fKFhbHP+CrvsQWY9af3LwUFJfn6Tvsv4O+S3Fb+0zj6lMVGEvL8CwYKi # excdFYmNcP7ntdAoGokLjzbaukz5m/8K6TT4JDVnK+ANuOaMmdbhIurwJ0I9JZTm # dHRbatGePu1+oDEzfbzL6Xu/OHBE0ZDxyKs6ijoIYn/ZcGNTTY3ugm2lBRDBcQZq # ELQdVTNYs6FwZvKhggNNMIICNQIBATCB+aGB0aSBzjCByzELMAkGA1UEBhMCVVMx # EzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoT # FU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjElMCMGA1UECxMcTWljcm9zb2Z0IEFtZXJp # Y2EgT3BlcmF0aW9uczEnMCUGA1UECxMeblNoaWVsZCBUU1MgRVNOOkEwMDAtMDVF # MC1EOTQ3MSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2aWNloiMK # AQEwBwYFKw4DAhoDFQCABol1u1wwwYgUtUowMnqYvbul3qCBgzCBgKR+MHwxCzAJ # BgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25k # MR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jv # c29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwMA0GCSqGSIb3DQEBCwUAAgUA6t//XjAi # GA8yMDI0MTExNDA0NDcyNloYDzIwMjQxMTE1MDQ0NzI2WjB0MDoGCisGAQQBhFkK # BAExLDAqMAoCBQDq3/9eAgEAMAcCAQACAh1AMAcCAQACAhLhMAoCBQDq4VDeAgEA # MDYGCisGAQQBhFkKBAIxKDAmMAwGCisGAQQBhFkKAwKgCjAIAgEAAgMHoSChCjAI # AgEAAgMBhqAwDQYJKoZIhvcNAQELBQADggEBAE7det78ut3iBOBs7dVElISMPBLQ # aGSyqGLXwHmUoyWrk5tJBNzbgYgf6BrwQ3t1OjTVMj9HDaCf6P8zaE8XIzuh9AOf # 6NEfy/VIlyJV9sr6PCG/awfBIJILGUiQIW7GCYF57PFpQDYBFEIs3PUrt3JrYd4p # KDQYNbb+qmEbX6vKCiMwPbyu13PwQcLBQFHjFN+UZO34ZBdlJeivQz3VelRYtkqb # Mf0zvu5DDNHhI4IVNxNHSweecd8ZqKR15ZdMXdxJbkrKk1QioYuz4b/SS4NjoQGY # bUEXQAGtD+fG+HCh1nHrKdvDZAZeXQJcIoVvdAwy4FD6jB8+7QwTFG2rXS0xggQN # MIIECQIBATCBkzB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQ # MA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9u # MSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMAITMwAAAevg # GGy1tu847QABAAAB6zANBglghkgBZQMEAgEFAKCCAUowGgYJKoZIhvcNAQkDMQ0G # CyqGSIb3DQEJEAEEMC8GCSqGSIb3DQEJBDEiBCClobMYIlR5o+2QAWXu8/k4NcHe # lTxub1XCE8OB8ODU2zCB+gYLKoZIhvcNAQkQAi8xgeowgecwgeQwgb0EIM63a75f # aQPhf8SBDTtk2DSUgIbdizXsz76h1JdhLCz4MIGYMIGApH4wfDELMAkGA1UEBhMC # VVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNV # BAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRp # bWUtU3RhbXAgUENBIDIwMTACEzMAAAHr4BhstbbvOO0AAQAAAeswIgQgcj33t7N1 # jz3PRt33DCY7Ia+XGW8U0DWyEbPz4buCEoQwDQYJKoZIhvcNAQELBQAEggIAJmFX # +Q36r/DfQ6fIPbRoSeJIpx75G+2M0daLJDwGkyQaT4vo9BLY5aoFuOwGqW3c2HaR # C5yNZUKWBF5ujGmIs6CT2+9SYeJsGWHlEZVL3V2w1Vo1/ycnJXTlBtxRMHxhxe83 # 5W7W9SrP7dt7Xk9ownM5q5mFuFN2xqijm7OxMuo4xa2CYiqX90uoxvG2YTMlPCK0 # EAOLVSrFCeO82UGZVGohK1F519UzBlWgXu5w03GcwgWNztpvMDNdgU6BSjUw+TON # h9mfTrEdjpJ8lte0qouOJpF3RP2diwXqL6nhkwDoytibCx4IOG+SmlB/mXSuh9zV # hIaIX+o0vNjT8AW/M2xmze0DdwmdpMwmhC5XAdGRoNhY7RNtIBFKHBdozNNoINZJ # gy+aODZUi6CRdtiqBe0Kri2PW/LUDZbWn2b419BD3UIj4ssUJlQDkOtgR59odd24 # VGCTScIdLuW/rW3nht0V5o/ylf0XpQqQ0fcyTMZ5dKc3YhgKVlvdKYZT6z8z7S+G # nukChCbmPJ7kJyEaQ1sRQusPfGhEn3XXrY0ZHl/G8Fi9dQl4I0jDUhrnt/f7fJ8d # NOA+Z347+dxZKEl0bLl0l8J/bKMO3itskTlOYKoilIrrQnZEVHFdS+9bO/VQRUIh # z9z4/YJANSuRAVljT3k/Vzcae5Cb9XAcJ/TjFAo= # SIG # End signature block |