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. # Code generated by Microsoft (R) AutoRest Code Generator.Changes may cause incorrect behavior and will be lost if the code # is regenerated. # ---------------------------------------------------------------------------------- [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 .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)] [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)] [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)] [Microsoft.Azure.PowerShell.Cmdlets.ConnectedKubernetes.Category('Body')] [System.String] # The geo-location where the resource lives ${Location}, [Parameter(ParameterSetName = 'Set', Mandatory)] [Parameter(ParameterSetName = 'SetDisableGateway', Mandatory)] [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] # Whether to enable or disable 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()] [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 } # Parse value from inputObject 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')) { if (-not $InputObject.GatewayEnabled) { $DisableGateway = -not $InputObject.GatewayEnabled } } if ((-not $PSBoundParameters.ContainsKey('GatewayResourceId')) -and (-not [String]::IsNullOrEmpty($InputObject.GatewayResourceId))) { $GatewayResourceId = $InputObject.GatewayResourceId $PSBoundParameters.Add('GatewayResourceId', $GatewayResourceId) } if (-not $PSBoundParameters.ContainsKey('DisableAutoUpgrade')) { $DisableAutoUpgrade = ($InputObject.ArcAgentProfileAgentAutoUpgrade -eq 'Disabled') } } if (-not [String]::IsNullOrEmpty($GatewayResourceId) -and -not $DisableGateway) { Write-Debug "Gateway enabled" $PSBoundParameters.Add('GatewayEnabled', $true) } # If DisableGateway is provided then set the gateway as disabled and remove gateway resourceId from parameters if ($DisableGateway) { Write-Debug "Gateway disabled" $Null = $PSBoundParameters.Remove('DisableGateway') $PSBoundParameters.Add('GatewayEnabled', $false) } $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-HelmReleaseNamespaces -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-HelmRepositoryAndModules -KubeConfig $KubeConfig -KubeContext $KubeContext -Location $Location -ProxyCert $ProxyCert -DisableAutoUpgrade $DisableAutoUpgrade -ContainerLogPath $ContainerLogPath -CustomLocationsOid $CustomLocationsOid Write-Debug "Processing Helm chart installation options." $options = "" if ($DisableAutoUpgrade -or ($ExistConnectedKubernetes.ArcAgentProfileAgentAutoUpgrade -eq 'Disabled')) { $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 the user does not provide proxy settings, or configuration settings, we shall use arc config of existing object $userProvidedArcConfiguration = ( ($null -ne $InputObject) -and ($InputObject.ArcAgentryConfiguration.Length > 0) ` -and (-not ([string]::IsNullOrEmpty($HttpProxy))) ` -and (-not ([string]::IsNullOrEmpty($HttpsProxy))) ` -and (-not ([string]::IsNullOrEmpty($NoProxy))) ` -and ((-not ([string]::IsNullOrEmpty($ProxyCert)))) ` -and ($PSBoundParameters.ContainsKey('ConfigurationSetting')) ` -and ($PSBoundParameters.ContainsKey('ConfigurationProtectedSetting'))) 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') } if ($userProvidedArcConfiguration) { $PSBoundParameters.Add('ArcAgentryConfiguration', $arcAgentryConfigs) } else { $PSBoundParameters.Add('ArcAgentryConfiguration', $ExistConnectedKubernetes.ArcAgentryConfiguration) } 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" -AsHashTable -Depth 10 # Whatif may return empty response if (-not $Response) { $Response = @{} } if (-not $Response.ContainsKey('properties')) { $Response['properties'] = @{} } if ($userProvidedArcConfiguration) { $Response['properties']['arcAgentryConfigurations'] = $arcAgentryConfigs } else { $Response['properties']['arcAgentryConfigurations'] = $ExistConnectedKubernetes.ArcAgentryConfiguration } # 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")) { try { $userValuesLocation = Join-Path $env:USERPROFILE ".azure\userValues.txt" helm get values azure-arc ` --debug ` --namespace $ReleaseInstallNamespace ` --kubeconfig $KubeConfig ` --kube-context $KubeContext > $userValuesLocation } catch { throw "Unable to get helm values" } } 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 ` --debug ` 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." } } } } } # SIG # Begin signature block # MIIoLQYJKoZIhvcNAQcCoIIoHjCCKBoCAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCCCPSxDOB6+MCgv # TUhWOpm3yMeuZ14OQgH6eaJNR5y0E6CCDXYwggX0MIID3KADAgECAhMzAAAEBGx0 # 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 # /Xmfwb1tbWrJUnMTDXpQzTGCGg0wghoJAgEBMIGVMH4xCzAJBgNVBAYTAlVTMRMw # EQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVN # aWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNp # Z25pbmcgUENBIDIwMTECEzMAAAQEbHQG/1crJ3IAAAAABAQwDQYJYIZIAWUDBAIB # BQCgga4wGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEO # MAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIHAWfmlkBeva/6tHN3v/UHcX # kGljHyOF/3bB8+T4mXzxMEIGCisGAQQBgjcCAQwxNDAyoBSAEgBNAGkAYwByAG8A # cwBvAGYAdKEagBhodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20wDQYJKoZIhvcNAQEB # BQAEggEAnaSvRn/LNKC1jddsXIaMSGZOUk7kthln3g7xIH+mCm2wCe3qITuGyWLt # r0wVvAdUSjfWmq4tXDVVyPDJzYglJLQZN1slyzC2oL8vUlsVxdWKJXARswWjS/VN # o578aV0DPKAfwsnU0S6l5qcaBM5bEAL11z/UtIUCdpTS6m35yBkOQyEX3tyu8ryB # HRfLQ5+0XJARG5tQ7AMNZpWBLwiTNylzHEcxOj2EIiAwJuCEqbvztt7Xhi22JNGY # y9GgTlW5bSdM5cDh49qy7DZx1WSesz0GbN2oTWG6uElFU3CAF+13uncAiTSN7x5H # G2qgIBuxmxDKaQPSu1bA/YHC9QSJv6GCF5cwgheTBgorBgEEAYI3AwMBMYIXgzCC # F38GCSqGSIb3DQEHAqCCF3AwghdsAgEDMQ8wDQYJYIZIAWUDBAIBBQAwggFSBgsq # hkiG9w0BCRABBKCCAUEEggE9MIIBOQIBAQYKKwYBBAGEWQoDATAxMA0GCWCGSAFl # AwQCAQUABCAI14zu2IcLMfTP9+ozyEvK4Jv02Yb9vd24yI/HmVO0XQIGZwf/s/n3 # GBMyMDI0MTAxNzA3NTQxNy45NTlaMASAAgH0oIHRpIHOMIHLMQswCQYDVQQGEwJV # UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UE # ChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSUwIwYDVQQLExxNaWNyb3NvZnQgQW1l # cmljYSBPcGVyYXRpb25zMScwJQYDVQQLEx5uU2hpZWxkIFRTUyBFU046OTYwMC0w # NUUwLUQ5NDcxJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2Wg # ghHtMIIHIDCCBQigAwIBAgITMwAAAe+JP1ahWMyo2gABAAAB7zANBgkqhkiG9w0B # AQsFADB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UE # BxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYD # VQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDAeFw0yMzEyMDYxODQ1 # NDhaFw0yNTAzMDUxODQ1NDhaMIHLMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2Fz # aGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENv # cnBvcmF0aW9uMSUwIwYDVQQLExxNaWNyb3NvZnQgQW1lcmljYSBPcGVyYXRpb25z # MScwJQYDVQQLEx5uU2hpZWxkIFRTUyBFU046OTYwMC0wNUUwLUQ5NDcxJTAjBgNV # BAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2UwggIiMA0GCSqGSIb3DQEB # AQUAA4ICDwAwggIKAoICAQCjC1jinwzgHwhOakZqy17oE4BIBKsm5kX4DUmCBWI0 # lFVpEiK5mZ2Kh59soL4ns52phFMQYGG5kypCipungwP9Nob4VGVE6aoMo5hZ9Nyt # XR5ZRgb9Z8NR6EmLKICRhD4sojPMg/RnGRTcdf7/TYvyM10jLjmLyKEegMHfvIwP # mM+AP7hzQLfExDdqCJ2u64Gd5XlnrFOku5U9jLOKk1y70c+Twt04/RLqruv1fGP8 # LmYmtHvrB4TcBsADXSmcFjh0VgQkX4zXFwqnIG8rgY+zDqJYQNZP8O1Yo4kSckHT # 43XC0oM40ye2+9l/rTYiDFM3nlZe2jhtOkGCO6GqiTp50xI9ITpJXi0vEek8AejT # 4PKMEO2bPxU63p63uZbjdN5L+lgIcCNMCNI0SIopS4gaVR4Sy/IoDv1vDWpe+I28 # /Ky8jWTeed0O3HxPJMZqX4QB3I6DnwZrHiKn6oE38tgBTCCAKvEoYOTg7r2lF0Iu # bt/3+VPvKtTCUbZPFOG8jZt9q6AFodlvQntiolYIYtqSrLyXAQIlXGhZ4gNcv4dv # 1YAilnbWA9CsnYh+OKEFr/4w4M69lI+yaoZ3L/t/UfXpT/+yc7hS/FolcmrGFJTB # YlS4nE1cuKblwZ/UOG26SLhDONWXGZDKMJKN53oOLSSk4ldR0HlsbT4heLlWlOEl # JQIDAQABo4IBSTCCAUUwHQYDVR0OBBYEFO1MWqKFwrCbtrw9P8A63bAVSJzLMB8G # A1UdIwQYMBaAFJ+nFV0AXmJdg/Tl0mWnG1M1GelyMF8GA1UdHwRYMFYwVKBSoFCG # Tmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY3Jvc29mdCUy # MFRpbWUtU3RhbXAlMjBQQ0ElMjAyMDEwKDEpLmNybDBsBggrBgEFBQcBAQRgMF4w # XAYIKwYBBQUHMAKGUGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY2Vy # dHMvTWljcm9zb2Z0JTIwVGltZS1TdGFtcCUyMFBDQSUyMDIwMTAoMSkuY3J0MAwG # A1UdEwEB/wQCMAAwFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwgwDgYDVR0PAQH/BAQD # AgeAMA0GCSqGSIb3DQEBCwUAA4ICAQAYGZa3aCDudbk9EVdkP8xcQGZuIAIPRx9K # 1CA7uRzBt80fC0aWkuYYhQMvHHJRHUobSM4Uw3zN7fHEN8hhaBDb9NRaGnFWdtHx # mJ9eMz6Jpn6KiIyi9U5Og7QCTZMl17n2w4eddq5vtk4rRWOVvpiDBGJARKiXWB9u # 2ix0WH2EMFGHqjIhjWUXhPgR4C6NKFNXHvWvXecJ2WXrJnvvQGXAfNJGETJZGpR4 # 1nUN3ijfiCSjFDxamGPsy5iYu904Hv9uuSXYd5m0Jxf2WNJSXkPGlNhrO27pPxgT # 111myAR61S3S2hc572zN9yoJEObE98Vy5KEM3ZX53cLefN81F1C9p/cAKkE6u9V6 # ryyl/qSgxu1UqeOZCtG/iaHSKMoxM7Mq4SMFsPT/8ieOdwClYpcw0CjZe5KBx2xL # a4B1neFib8J8/gSosjMdF3nHiyHx1YedZDtxSSgegeJsi0fbUgdzsVMJYvqVw52W # qQNu0GRC79ZuVreUVKdCJmUMBHBpTp6VFopL0Jf4Srgg+zRD9iwbc9uZrn+89odp # InbznYrnPKHiO26qe1ekNwl/d7ro2ItP/lghz0DoD7kEGeikKJWHdto7eVJoJhkr # UcanTuUH08g+NYwG6S+PjBSB/NyNF6bHa/xR+ceAYhcjx0iBiv90Mn0JiGfnA2/h # Lj5evhTcAjCCB3EwggVZoAMCAQICEzMAAAAVxedrngKbSZkAAAAAABUwDQYJKoZI # 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/ZcGNTTY3ugm2lBRDBcQZqELQdVTNYs6FwZvKhggNQ # MIICOAIBATCB+aGB0aSBzjCByzELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hp # bmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jw # b3JhdGlvbjElMCMGA1UECxMcTWljcm9zb2Z0IEFtZXJpY2EgT3BlcmF0aW9uczEn # MCUGA1UECxMeblNoaWVsZCBUU1MgRVNOOjk2MDAtMDVFMC1EOTQ3MSUwIwYDVQQD # ExxNaWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2aWNloiMKAQEwBwYFKw4DAhoDFQBL # cI81gxbea1Ex2mFbXx7ck+0g/6CBgzCBgKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYD # VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1w # IFBDQSAyMDEwMA0GCSqGSIb3DQEBCwUAAgUA6rsPXDAiGA8yMDI0MTAxNzA0MjE0 # OFoYDzIwMjQxMDE4MDQyMTQ4WjB3MD0GCisGAQQBhFkKBAExLzAtMAoCBQDquw9c # AgEAMAoCAQACAhKDAgH/MAcCAQACAhPCMAoCBQDqvGDcAgEAMDYGCisGAQQBhFkK # BAIxKDAmMAwGCisGAQQBhFkKAwKgCjAIAgEAAgMHoSChCjAIAgEAAgMBhqAwDQYJ # KoZIhvcNAQELBQADggEBADAyIv2U6Cq/fHOtH+gvaeHDhYbMR0DDp2iZvcwgqx1W # xUU/aAS4reQHpqUw0cCVtl2cc35R2MrdKTaZLtpo4/oTGJlRBtpa6jbr4cI5FneT # HHGs9gPR+ujHI7EGwfGZH8hbHABoe7rsegXr+rfz4BDUFKHl1mzh988npLXwf2iC # 7456dct2AJyY7QeG5rzWJyPy7j6WJy5lMxDxA8u2h5iAG92L34YbZnRHHgWnOsfK # zj1DhwNkJssFZvVhNWzKaBkEarh5YpaqS4JOUWxMZt2M0vfY2OtuSmcDqF5NBvbt # 9W2iH3m9zy2JwbMXUZW2dkpnFI/VVCXsoKYaNQfnKdwxggQNMIIECQIBATCBkzB8 # MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVk # bW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1N # aWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMAITMwAAAe+JP1ahWMyo2gABAAAB # 7zANBglghkgBZQMEAgEFAKCCAUowGgYJKoZIhvcNAQkDMQ0GCyqGSIb3DQEJEAEE # MC8GCSqGSIb3DQEJBDEiBCBWEMuXw9BNB2d8FvLEDuOni0q3Esnd61INc4P1rgMc # szCB+gYLKoZIhvcNAQkQAi8xgeowgecwgeQwgb0EIPBhKEW4Fo3wUz09NQx2a0Db # cdsX8jovM5LizHmnyX+jMIGYMIGApH4wfDELMAkGA1UEBhMCVVMxEzARBgNVBAgT # Cldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29m # dCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENB # IDIwMTACEzMAAAHviT9WoVjMqNoAAQAAAe8wIgQgqx6ZyCJ8iMdOeGoIiC7NRUdu # AqCUNrkDstfgGmaOIBwwDQYJKoZIhvcNAQELBQAEggIATmcVBVQdCzzLfdcLEbpm # osZ5K3FQxjSTIHu1QZ+00i91kOMLcJF6EkwriLnD9kiI8nNayOlBBzorllxt5md8 # 7m04zWcyCtDNsUxem8CPDEYXhCPFgLhRZsABoya/L+oP91hfy2PEEkurz0IAjR3u # 6d95wo0CX1yjWRkZGIazsi4V1qCBIXhjMQVQYWdYr/UuVzfmsuFY5qnO5wXpzHxA # uMVOStDFvlCSh2supNORFRQmx/Q5WQOU5rAJXxk62RSW7zQV3a3Lm42DH/fOike4 # lBnZDn3VPLDgIwMJO1wC1xRA83+CxGNvyTkVuETIrOn2y72XRrj7Uu/ERP2Kn2ti # lQJuTujbmqdr4HsKMs0O2d6CUeWO1tvrRgsGLB0Z49k+Ow6g566FhLbx4VzpGHC2 # hWC9SKJvPz8Ky5O2m4Rqaegi5d0+HpUaatoEncn9mzQaCDa1ClXPlSth18xp31ip # GmfKL1KwdTtCl0DtzN3eyvbcG+VNvA9jyH9zNLVC32DUwQ+DjfPRFn8YIt6yiigM # JZGUMdCMZj/6VH4hBqVHowRnqkUczxB7NgatFExaB1XOHmHwZQyFFyOq/2X0On+2 # Zu9YtkQ5Aql65zYAM9ctWDqnryd2GPZOFQYroNRQf6CnClYKHDC3qp6IxNHW2ncR # 8IKm9RjSzGgXgCfaIUMN7qE= # SIG # End signature block |