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-Output "Updating the connected cluster resource...." $Response = Az.ConnectedKubernetes.internal\Set-AzConnectedKubernetes @PSBoundParameters if ((-not $WhatIfPreference) -and (-not $Response)) { Write-Error "Failed to update the 'Kubernetes - Azure Arc' resource" return } $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: $Response" $Response = ConvertFrom-Json "$Response" $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-Output "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-Output "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) } catch { throw "Unable to install helm release" } Return $Response } if ($PSCmdlet.ShouldProcess($ClusterName, "Check agent state of the connected cluster")) { if ($PSBoundParameters.ContainsKey('OidcIssuerProfileEnabled') -or $PSBoundParameters.ContainsKey('WorkloadIdentityEnabled') ) { $ExistConnectedKubernetes = Get-AzConnectedKubernetes -ResourceGroupName $ResourceGroupName -ClusterName $ClusterName @CommonPSBoundParameters Write-Output "Cluster configuration is in progress..." $timeout = [datetime]::Now.AddMinutes(60) while (($ExistConnectedKubernetes.ArcAgentProfileAgentState -ne "Succeeded") -and ($ExistConnectedKubernetes.ArcAgentProfileAgentState -ne "Failed") -and ([datetime]::Now -lt $timeout)) { Start-Sleep -Seconds 30 $ExistConnectedKubernetes = Get-AzConnectedKubernetes -ResourceGroupName $ResourceGroupName -ClusterName $ClusterName @CommonPSBoundParameters } if ($ExistConnectedKubernetes.ArcAgentProfileAgentState -eq "Succeeded") { Write-Output "Cluster configuration succeeded." } elseif ($ExistConnectedKubernetes.ArcAgentProfileAgentState -eq "Failed") { Write-Error "Cluster configuration failed." } else { Write-Error "Cluster configuration timed out after 60 minutes." } } } } } 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 # MIIoKgYJKoZIhvcNAQcCoIIoGzCCKBcCAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCAzsrKUGFj5jcto # YzAZKn8Z1wDcW937Wlk/e3kLayMb+6CCDXYwggX0MIID3KADAgECAhMzAAAEBGx0 # Bv9XKydyAAAAAAQEMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD # VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p # bmcgUENBIDIwMTEwHhcNMjQwOTEyMjAxMTE0WhcNMjUwOTExMjAxMTE0WjB0MQsw # CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u # ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB # AQC0KDfaY50MDqsEGdlIzDHBd6CqIMRQWW9Af1LHDDTuFjfDsvna0nEuDSYJmNyz # NB10jpbg0lhvkT1AzfX2TLITSXwS8D+mBzGCWMM/wTpciWBV/pbjSazbzoKvRrNo # DV/u9omOM2Eawyo5JJJdNkM2d8qzkQ0bRuRd4HarmGunSouyb9NY7egWN5E5lUc3 # a2AROzAdHdYpObpCOdeAY2P5XqtJkk79aROpzw16wCjdSn8qMzCBzR7rvH2WVkvF # HLIxZQET1yhPb6lRmpgBQNnzidHV2Ocxjc8wNiIDzgbDkmlx54QPfw7RwQi8p1fy # 4byhBrTjv568x8NGv3gwb0RbAgMBAAGjggFzMIIBbzAfBgNVHSUEGDAWBgorBgEE # AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQU8huhNbETDU+ZWllL4DNMPCijEU4w # RQYDVR0RBD4wPKQ6MDgxHjAcBgNVBAsTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEW # MBQGA1UEBRMNMjMwMDEyKzUwMjkyMzAfBgNVHSMEGDAWgBRIbmTlUAXTgqoXNzci # tW2oynUClTBUBgNVHR8ETTBLMEmgR6BFhkNodHRwOi8vd3d3Lm1pY3Jvc29mdC5j # b20vcGtpb3BzL2NybC9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDctMDguY3JsMGEG # CCsGAQUFBwEBBFUwUzBRBggrBgEFBQcwAoZFaHR0cDovL3d3dy5taWNyb3NvZnQu # Y29tL3BraW9wcy9jZXJ0cy9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDctMDguY3J0 # MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggIBAIjmD9IpQVvfB1QehvpC # Ge7QeTQkKQ7j3bmDMjwSqFL4ri6ae9IFTdpywn5smmtSIyKYDn3/nHtaEn0X1NBj # L5oP0BjAy1sqxD+uy35B+V8wv5GrxhMDJP8l2QjLtH/UglSTIhLqyt8bUAqVfyfp # h4COMRvwwjTvChtCnUXXACuCXYHWalOoc0OU2oGN+mPJIJJxaNQc1sjBsMbGIWv3 # cmgSHkCEmrMv7yaidpePt6V+yPMik+eXw3IfZ5eNOiNgL1rZzgSJfTnvUqiaEQ0X # dG1HbkDv9fv6CTq6m4Ty3IzLiwGSXYxRIXTxT4TYs5VxHy2uFjFXWVSL0J2ARTYL # E4Oyl1wXDF1PX4bxg1yDMfKPHcE1Ijic5lx1KdK1SkaEJdto4hd++05J9Bf9TAmi # u6EK6C9Oe5vRadroJCK26uCUI4zIjL/qG7mswW+qT0CW0gnR9JHkXCWNbo8ccMk1 # sJatmRoSAifbgzaYbUz8+lv+IXy5GFuAmLnNbGjacB3IMGpa+lbFgih57/fIhamq # 5VhxgaEmn/UjWyr+cPiAFWuTVIpfsOjbEAww75wURNM1Imp9NJKye1O24EspEHmb # DmqCUcq7NqkOKIG4PVm3hDDED/WQpzJDkvu4FrIbvyTGVU01vKsg4UfcdiZ0fQ+/ # V0hf8yrtq9CkB8iIuk5bBxuPMIIHejCCBWKgAwIBAgIKYQ6Q0gAAAAAAAzANBgkq # 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 # Z25pbmcgUENBIDIwMTECEzMAAAQEbHQG/1crJ3IAAAAABAQwDQYJYIZIAWUDBAIB # BQCgga4wGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEO # MAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEINMkPOwMauwGQ8ajZJViPYXU # PComcIyiNXjb13TKstpIMEIGCisGAQQBgjcCAQwxNDAyoBSAEgBNAGkAYwByAG8A # cwBvAGYAdKEagBhodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20wDQYJKoZIhvcNAQEB # BQAEggEASmfwPf4atoIcwZ3FeJ0RMGdz8+Da38OxWsvaHl6mhA6JDDx6qNpXfovJ # 9qSjZ82LnJiqYwVTwUKcGNzCXJH8sKbGCKH1vRvy0Z66xDVYZCxESgHQhCP1yVhx # e2NW9vkqSLY5X45x3JLMkJmY+Ns9scD9L+sAE7aoR+AHGCg1334rG3Ob2vRp2hhT # HYKTiBsP5ySbp6AiRLGcxP99mejA6YJYmCt5andAEHhZ7g3SBpUc9Oh2oNzyuEIK # 4Xn8M6qnDmRtelURDeXyIycxBQ0ttZUaSXTQks536HpDrBH5xi5CNGV2bRGBDnok # 6S8T3N/8hB6fKa69aXb5OM8g7rRgaKGCF5QwgheQBgorBgEEAYI3AwMBMYIXgDCC # F3wGCSqGSIb3DQEHAqCCF20wghdpAgEDMQ8wDQYJYIZIAWUDBAIBBQAwggFSBgsq # hkiG9w0BCRABBKCCAUEEggE9MIIBOQIBAQYKKwYBBAGEWQoDATAxMA0GCWCGSAFl # AwQCAQUABCDDNRiEI0UVC+OoEn7n5vcfcvlnS1Fm1KUHrWZolQpOCgIGZxp41HPi # GBMyMDI0MTEwODAyNTkyMS43MTlaMASAAgH0oIHRpIHOMIHLMQswCQYDVQQGEwJV # UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UE # ChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSUwIwYDVQQLExxNaWNyb3NvZnQgQW1l # cmljYSBPcGVyYXRpb25zMScwJQYDVQQLEx5uU2hpZWxkIFRTUyBFU046OTIwMC0w # NUUwLUQ5NDcxJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2Wg # ghHqMIIHIDCCBQigAwIBAgITMwAAAecujy+TC08b6QABAAAB5zANBgkqhkiG9w0B # AQsFADB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UE # BxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYD # VQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDAeFw0yMzEyMDYxODQ1 # MTlaFw0yNTAzMDUxODQ1MTlaMIHLMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2Fz # aGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENv # cnBvcmF0aW9uMSUwIwYDVQQLExxNaWNyb3NvZnQgQW1lcmljYSBPcGVyYXRpb25z # MScwJQYDVQQLEx5uU2hpZWxkIFRTUyBFU046OTIwMC0wNUUwLUQ5NDcxJTAjBgNV # BAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2UwggIiMA0GCSqGSIb3DQEB # AQUAA4ICDwAwggIKAoICAQDCV58v4IuQ659XPM1DtaWMv9/HRUC5kdiEF89YBP6/ # Rn7kjqMkZ5ESemf5Eli4CLtQVSefRpF1j7S5LLKisMWOGRaLcaVbGTfcmI1vMRJ1 # tzMwCNIoCq/vy8WH8QdV1B/Ab5sK+Q9yIvzGw47TfXPE8RlrauwK/e+nWnwMt060 # akEZiJJz1Vh1LhSYKaiP9Z23EZmGETCWigkKbcuAnhvh3yrMa89uBfaeHQZEHGQq # dskM48EBcWSWdpiSSBiAxyhHUkbknl9PPztB/SUxzRZjUzWHg9bf1mqZ0cIiAWC0 # EjK7ONhlQfKSRHVLKLNPpl3/+UL4Xjc0Yvdqc88gOLUr/84T9/xK5r82ulvRp2A8 # /ar9cG4W7650uKaAxRAmgL4hKgIX5/0aIAsbyqJOa6OIGSF9a+DfXl1LpQPNKR79 # 2scF7tjD5WqwIuifS9YUiHMvRLjjKk0SSCV/mpXC0BoPkk5asfxrrJbCsJePHSOE # blpJzRmzaP6OMXwRcrb7TXFQOsTkKuqkWvvYIPvVzC68UM+MskLPld1eqdOOMK7S # bbf2tGSZf3+iOwWQMcWXB9gw5gK3AIYK08WkJJuyzPqfitgubdRCmYr9CVsNOuW+ # wHDYGhciJDF2LkrjkFUjUcXSIJd9f2ssYitZ9CurGV74BQcfrxjvk1L8jvtN7mul # IwIDAQABo4IBSTCCAUUwHQYDVR0OBBYEFM/+4JiAnzY4dpEf/Zlrh1K73o9YMB8G # A1UdIwQYMBaAFJ+nFV0AXmJdg/Tl0mWnG1M1GelyMF8GA1UdHwRYMFYwVKBSoFCG # Tmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY3Jvc29mdCUy # MFRpbWUtU3RhbXAlMjBQQ0ElMjAyMDEwKDEpLmNybDBsBggrBgEFBQcBAQRgMF4w # XAYIKwYBBQUHMAKGUGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY2Vy # dHMvTWljcm9zb2Z0JTIwVGltZS1TdGFtcCUyMFBDQSUyMDIwMTAoMSkuY3J0MAwG # A1UdEwEB/wQCMAAwFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwgwDgYDVR0PAQH/BAQD # AgeAMA0GCSqGSIb3DQEBCwUAA4ICAQB0ofDbk+llWi1cC6nsfie5Jtp09o6b6ARC # pvtDPq2KFP+hi+UNNP7LGciKuckqXCmBTFIhfBeGSxvk6ycokdQr3815pEOaYWTn # HvQ0+8hKy86r1F4rfBu4oHB5cTy08T4ohrG/OYG/B/gNnz0Ol6v7u/qEjz48zXZ6 # ZlxKGyZwKmKZWaBd2DYEwzKpdLkBxs6A6enWZR0jY+q5FdbV45ghGTKgSr5ECAOn # LD4njJwfjIq0mRZWwDZQoXtJSaVHSu2lHQL3YHEFikunbUTJfNfBDLL7Gv+sTmRi # DZky5OAxoLG2gaTfuiFbfpmSfPcgl5COUzfMQnzpKfX6+FkI0QQNvuPpWsDU8sR+ # uni2VmDo7rmqJrom4ihgVNdLaMfNUqvBL5ZiSK1zmaELBJ9a+YOjE5pmSarW5sGb # n7iVkF2W9JQIOH6tGWLFJS5Hs36zahkoHh8iD963LeGjZqkFusKaUW72yMj/yxTe # GEDOoIr35kwXxr1Uu+zkur2y+FuNY0oZjppzp95AW1lehP0xaO+oBV1XfvaCur/B # 5PVAp2xzrosMEUcAwpJpio+VYfIufGj7meXcGQYWA8Umr8K6Auo+Jlj8IeFS6lSv # KhqQpmdBzAMGqPOQKt1Ow3ZXxehK7vAiim3ZiALlM0K546k0sZrxdZPgpmz7O8w9 # gHLuyZAQezCCB3EwggVZoAMCAQICEzMAAAAVxedrngKbSZkAAAAAABUwDQYJKoZI # 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 # MCUGA1UECxMeblNoaWVsZCBUU1MgRVNOOjkyMDAtMDVFMC1EOTQ3MSUwIwYDVQQD # ExxNaWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2aWNloiMKAQEwBwYFKw4DAhoDFQCz # cgTnGasSwe/dru+cPe1NF/vwQ6CBgzCBgKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYD # VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1w # IFBDQSAyMDEwMA0GCSqGSIb3DQEBCwUAAgUA6tdrNjAiGA8yMDI0MTEwNzE2Mzcx # MFoYDzIwMjQxMTA4MTYzNzEwWjB0MDoGCisGAQQBhFkKBAExLDAqMAoCBQDq12s2 # AgEAMAcCAQACAgq1MAcCAQACAhPGMAoCBQDq2Ly2AgEAMDYGCisGAQQBhFkKBAIx # KDAmMAwGCisGAQQBhFkKAwKgCjAIAgEAAgMHoSChCjAIAgEAAgMBhqAwDQYJKoZI # hvcNAQELBQADggEBAHB5Z5Gc7og/HltP5nNCjZgZ0dVZNpnmh79thv+Zru5sPGjb # gU23TVR6CLgWpIdKF9RpIZbQVJyfPPm+yWbrhXxNfQm98wT+eUICpZDWHp0Bn8+M # bD2CqEnSSwo6E3nXkzF6LgjGZ6oyz71MFKbgJEH7Z93fXoKNhPyGT8R9+4rr/LS7 # yAxFLpfVl65t0lKPrXCR/XwEYpjdunx+qVp2o+TLm0d3vSg7wbot+g/J5Lwftbj3 # nBM0ujxBDpCAUZtiw2e7qX5p7SwvklU1I/H4HVaUboflP+yWHfiYnbR5bISh5FT+ # wMPeW8bb/dfq9eTyWqhWSegEh2bvGfdvvsaR4ZAxggQNMIIECQIBATCBkzB8MQsw # CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u # ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNy # b3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMAITMwAAAecujy+TC08b6QABAAAB5zAN # BglghkgBZQMEAgEFAKCCAUowGgYJKoZIhvcNAQkDMQ0GCyqGSIb3DQEJEAEEMC8G # CSqGSIb3DQEJBDEiBCAB5wW0W7NOh/QlfGI3KItnEVhChd1FLadR+z46jDel0TCB # +gYLKoZIhvcNAQkQAi8xgeowgecwgeQwgb0EIOU2XQ12aob9DeDFXM9UFHeEX74F # v0ABvQMG7qC51nOtMIGYMIGApH4wfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldh # c2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBD # b3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIw # MTACEzMAAAHnLo8vkwtPG+kAAQAAAecwIgQgxzZsSCnvuY6fuxTep/FAUuvxyhOK # 2TK0fR6r5XpudS8wDQYJKoZIhvcNAQELBQAEggIAXVRW4XY+QU7FZ0MbQMLmUpGH # mjjybJjcuA/QV5r1PP3lhNF7QG3VCyavk/9Z74GlYRvnK4aPxKQ+SMFY3CFFK8U6 # v+ANzPIrtZygzAxUFIHOC1rSFRq4Ewure45My9+azomAylUxnhh4gzh20AY5bBzH # DPd1OEpZ/6kOla5zRmOBsBxJ8OLIuvX48fRrXWmj2Y85nNEG/qGDbz92PCbQhoiA # 5DS6r52PvwAYiUhEOuZv/wniMgrfJHaXJJDG42dBHN2rNxjbTJcqWL0TMY2oZoNy # JkSMEiuj2AUp88pigNK61Mo0MwG4Y4/jyMmOtEqQN3QS/hguuWsBOSyzj/wtNwkf # fIflj5oDe2VGBiT6S91GUwDEc+iSz295fr/B8yPFiYsw8zNi0tyPQBk188q3C5jd # 1M7oMLV+Jw1gpPWMmBrJ43LQsPAPKSwxynFy75xRa30siUDp6D4fKByWE42vhZcB # cDCoLgxt9bePL+YRa59F3Dh6AfJsAjj/b0P31Ak5nJyCGoPoEekd1OSJrrIleBOD # oy7x5ghbZaEYvIDsLX4Jhdho+eIeQimIRS403FCTULi8QYjyXKZEmTTVmbBjyOSz # owwDFl8HJ3Ww55/vWDSS/d70Tv0YzQkUeGfgyXcnybsxBdjq4JiA43WOScfa0zXw # c3hKxJJE2kENxdwmazw= # SIG # End signature block |