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.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.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.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()] [AllowEmptyCollection()] [Microsoft.Azure.PowerShell.Cmdlets.ConnectedKubernetes.Category('Body')] [System.String[]] # The list of AAD group object IDs that will have admin role of the cluster. ${AadProfileAdminGroupObjectID}, [Parameter()] [Microsoft.Azure.PowerShell.Cmdlets.ConnectedKubernetes.Category('Body')] [System.Management.Automation.SwitchParameter] # Whether to enable Azure RBAC for Kubernetes authorization. ${AadProfileEnableAzureRbac}, [Parameter()] [Microsoft.Azure.PowerShell.Cmdlets.ConnectedKubernetes.Category('Body')] [System.String] # The AAD tenant ID to use for authentication. # If not specified, will use the tenant of the deployment subscription. ${AadProfileTenantId}, [Parameter()] [Microsoft.Azure.PowerShell.Cmdlets.ConnectedKubernetes.PSArgumentCompleterAttribute("Enabled", "Disabled")] [Microsoft.Azure.PowerShell.Cmdlets.ConnectedKubernetes.Category('Body')] [System.String] # Indicates whether the Arc agents on the be upgraded automatically to the latest version. # Defaults to Enabled. ${ArcAgentProfileAgentAutoUpgrade}, [Parameter()] [AllowEmptyCollection()] [Microsoft.Azure.PowerShell.Cmdlets.ConnectedKubernetes.Category('Body')] [Microsoft.Azure.PowerShell.Cmdlets.ConnectedKubernetes.Models.IAgentError[]] # List of arc agentry and system components errors on the cluster resource. ${ArcAgentProfileAgentError}, [Parameter()] [Microsoft.Azure.PowerShell.Cmdlets.ConnectedKubernetes.Category('Body')] [System.String] # Version of the Arc agents to be installed on the cluster resource ${ArcAgentProfileDesiredAgentVersion}, [Parameter()] [AllowEmptyCollection()] [Microsoft.Azure.PowerShell.Cmdlets.ConnectedKubernetes.Category('Body')] [Microsoft.Azure.PowerShell.Cmdlets.ConnectedKubernetes.Models.ISystemComponent[]] # List of system extensions that are installed on the cluster resource. ${ArcAgentProfileSystemComponent}, [Parameter()] [AllowEmptyCollection()] [Microsoft.Azure.PowerShell.Cmdlets.ConnectedKubernetes.Category('Body')] [Microsoft.Azure.PowerShell.Cmdlets.ConnectedKubernetes.Models.IArcAgentryConfigurations[]] # Configuration settings for customizing the behavior of the connected cluster. ${ArcAgentryConfiguration}, [Parameter()] [Microsoft.Azure.PowerShell.Cmdlets.ConnectedKubernetes.PSArgumentCompleterAttribute("True", "False", "NotApplicable")] [Microsoft.Azure.PowerShell.Cmdlets.ConnectedKubernetes.Category('Body')] [System.String] # 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.Management.Automation.SwitchParameter] # Indicates whether the gateway for arc router connectivity is enabled. ${GatewayEnabled}, [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.PSArgumentCompleterAttribute("ProvisionedCluster")] [Microsoft.Azure.PowerShell.Cmdlets.ConnectedKubernetes.Category('Body')] [System.String] # The kind of connected cluster. ${Kind}, [Parameter()] [Microsoft.Azure.PowerShell.Cmdlets.ConnectedKubernetes.Category('Body')] [System.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. ${PrivateLinkScopeResourceId}, [Parameter()] [Microsoft.Azure.PowerShell.Cmdlets.ConnectedKubernetes.PSArgumentCompleterAttribute("Enabled", "Disabled")] [Microsoft.Azure.PowerShell.Cmdlets.ConnectedKubernetes.Category('Body')] [System.String] # Property which describes the state of private link on a connected cluster resource. ${PrivateLinkState}, [Parameter()] [Microsoft.Azure.PowerShell.Cmdlets.ConnectedKubernetes.PSArgumentCompleterAttribute("Succeeded", "Failed", "Canceled", "Provisioning", "Updating", "Deleting", "Accepted")] [Microsoft.Azure.PowerShell.Cmdlets.ConnectedKubernetes.Category('Body')] [System.String] # 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.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 DefaultProfile parameter is not functional. # Use the SubscriptionId parameter when available if executing the cmdlet against a different subscription. ${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 for the proxy server to use ${Proxy}, [Parameter(DontShow)] [ValidateNotNull()] [Microsoft.Azure.PowerShell.Cmdlets.ConnectedKubernetes.Category('Runtime')] [System.Management.Automation.PSCredential] # Credentials for a proxy server to use for the remote call ${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') } $PSBoundParameters.Add('EnableSystemAssignedIdentity', $true) #Region check helm install Confirm-HelmVersion ` -KubeConfig $KubeConfig ` -Verbose:($PSCmdlet.MyInvocation.BoundParameters["Verbose"].IsPresent -eq $true) ` -Debug:($PSCmdlet.MyInvocation.BoundParameters["Debug"].IsPresent -eq $true) #EndRegion $helmClientLocation = 'helm' #Region get release namespace $ReleaseNamespaces = Get-HelmReleaseNamespace -KubeConfig $KubeConfig -KubeContext $KubeContext $ReleaseNamespace = $ReleaseNamespaces['ReleaseNamespace'] $ReleaseInstallNamespace = $ReleaseNamespaces['ReleaseInstallNamespace'] #Endregion #Region validate release namespace if (-not ([string]::IsNullOrEmpty($ReleaseNamespace))) { $Configmap = kubectl get configmap --namespace azure-arc azure-clusterconfig -o json --kubeconfig $KubeConfig | ConvertFrom-Json $ConfigmapRgName = $Configmap.data.AZURE_RESOURCE_GROUP $ConfigmapClusterName = $Configmap.data.AZURE_RESOURCE_NAME try { $ExistConnectedKubernetes = Get-AzConnectedKubernetes ` -ResourceGroupName $ConfigmapRgName ` -ClusterName $ConfigmapClusterName ` @CommonPSBoundParameters ` -ErrorAction 'silentlycontinue' $PSBoundParameters.Add('AgentPublicKeyCertificate', $ExistConnectedKubernetes.AgentPublicKeyCertificate) if (($ResourceGroupName.ToLower() -ne $ConfigmapRgName.ToLower()) -or ($ClusterName.ToLower() -ne $ConfigmapClusterName.ToLower())) { Write-Error "The provided cluster name and rg correspond to different cluster" return } } catch { Write-Error "The corresponding connected cluster resource does not exist" return } } else { Write-Error "The azure-arc release namespace couldn't be retrieved, which implies that the kubernetes cluster has not been onboarded to azure-arc." return } #Endregion # Adding Helm repo $RegistryPath = Set-HelmModulesAndRepository -KubeConfig $KubeConfig -KubeContext $KubeContext -Location $Location Write-Debug "Processing Helm chart installation options." $options = "" if ($DisableAutoUpgrade) { $Null = $PSBoundParameters.Remove('DisableAutoUpgrade') $PSBoundParameters.Add('ArcAgentProfileAgentAutoUpgrade', 'Disabled') } if (-not ([string]::IsNullOrEmpty($ContainerLogPath))) { $options += " --set systemDefaultValues.fluent-bit.containerLogPath=$ContainerLogPath" $Null = $PSBoundParameters.Remove('ContainerLogPath') } if (-not ([string]::IsNullOrEmpty($KubeConfig))) { $options += " --kubeconfig $KubeConfig" } if (-not ([string]::IsNullOrEmpty($KubeContext))) { $options += " --kube-context $KubeContext" } if (-not ([string]::IsNullOrEmpty($CustomLocationsOid))) { $options += " --set systemDefaultValues.customLocations.oid=$CustomLocationsOid" $options += " --set systemDefaultValues.customLocations.enabled=true" } if ((-not ([string]::IsNullOrEmpty($Proxy))) -and (-not $PSBoundParameters.ContainsKey('ProxyCredential'))) { if (-not ([string]::IsNullOrEmpty($Proxy.UserInfo))) { try { $userInfo = $Proxy.UserInfo -Split ':' $pass = ConvertTo-SecureString $userInfo[1] -AsPlainText -Force $ProxyCredential = New-Object System.Management.Automation.PSCredential ($userInfo[0] , $pass) $PSBoundParameters.Add('ProxyCredential', $ProxyCredential) } catch { throw "Please set ProxyCredential or provide username and password in the Proxy parameter" } } else { Write-Warning "If the proxy is a private proxy, pass ProxyCredential parameter or provide username and password in the Proxy parameter" } } #Endregion #Region Deal with configuration settings and protected settings if ($null -eq $ConfigurationSetting) { $ConfigurationSetting = @{} } if ($null -eq $ConfigurationProtectedSetting) { $ConfigurationProtectedSetting = @{} } if ($null -ne $InputObject) { foreach ($arcAgentConfig in $InputObject.ArcAgentryConfiguration) { $ConfigurationSetting[$arcAgentConfig.feature] = $arcAgentConfig.settings $ConfigurationProtectedSetting[$arcAgentConfig.feature] = $arcAgentConfig.protectedSettings } } if (-not $ConfigurationProtectedSetting.ContainsKey("proxy")) { $ConfigurationProtectedSetting["proxy"] = @{} } if (-not ([string]::IsNullOrEmpty($HttpProxy))) { $HttpProxyStr = $HttpProxy.ToString() $HttpProxyStr = $HttpProxyStr -replace ',', '\,' $HttpProxyStr = $HttpProxyStr -replace '/', '\/' $ConfigurationProtectedSetting["proxy"]["http_proxy"] = $HttpProxyStr # Note how we are removing k8s parameters from the list of parameters # to pass to the internal (creates ARM object) command. $Null = $PSBoundParameters.Remove('HttpProxy') } if (-not ([string]::IsNullOrEmpty($HttpsProxy))) { $HttpsProxyStr = $HttpsProxy.ToString() $HttpsProxyStr = $HttpsProxyStr -replace ',', '\,' $HttpsProxyStr = $HttpsProxyStr -replace '/', '\/' $ConfigurationProtectedSetting["proxy"]["https_proxy"] = $HttpsProxyStr $Null = $PSBoundParameters.Remove('HttpsProxy') } if (-not ([string]::IsNullOrEmpty($NoProxy))) { $NoProxy = $NoProxy -replace ',', '\,' $NoProxy = $NoProxy -replace '/', '\/' $ConfigurationProtectedSetting["proxy"]["no_proxy"] = $NoProxy $Null = $PSBoundParameters.Remove('NoProxy') } try { if ((-not ([string]::IsNullOrEmpty($ProxyCert))) -and (Test-Path $ProxyCert)) { $ConfigurationProtectedSetting["proxy"]["proxy_cert"] = $ProxyCert } } catch { throw "Unable to find ProxyCert from file path" } $RedactedProtectedConfiguration = @{} # Duplicate the protected settings into the settings. foreach ($feature in $ConfigurationProtectedSetting.Keys) { if (-not $RedactedProtectedConfiguration.ContainsKey($feature)) { $RedactedProtectedConfiguration[$feature] = @{} } foreach ($setting in $ConfigurationProtectedSetting[$feature].Keys) { $RedactedProtectedConfiguration[$feature][$setting] = "${ProtectedSettingsPlaceholderValue}:${feature}:${setting}" } } #Endregion # A lot of what follows relies on knowing the cloud we are using and the # various endpoints so get that information now. $cloudMetadata = Get-AzCloudMetadata ` -Verbose:($PSCmdlet.MyInvocation.BoundParameters["Verbose"].IsPresent -eq $true) ` -Debug:($PSCmdlet.MyInvocation.BoundParameters["Debug"].IsPresent -eq $true) # Perform DP health check $configDpinfo = Get-ConfigDPEndpoint ` -location $Location ` -Cloud $cloudMetadata ` -Verbose:($PSCmdlet.MyInvocation.BoundParameters["Verbose"].IsPresent -eq $true) ` -Debug:($PSCmdlet.MyInvocation.BoundParameters["Debug"].IsPresent -eq $true) $configDPEndpoint = $configDpInfo.configDPEndpoint # If the health check fails (not 200 response), an exception is thrown # so we can ignore the output. $null = Invoke-ConfigDPHealthCheck ` -configDPEndpoint $configDPEndpoint ` -Verbose:($PSCmdlet.MyInvocation.BoundParameters["Verbose"].IsPresent -eq $true) ` -Debug:($PSCmdlet.MyInvocation.BoundParameters["Debug"].IsPresent -eq $true) # This call does the "pure ARM" update of the ARM objects. Write-Debug "Updating Connected Kubernetes ARM objects." # Process the Arc agentry settings and protected settings # Create any empty array of IArcAgentryConfigurations. # shortened name to avoid class with type name. # # **NOTE** The Swagger naming does NOT match the names that will be used # in the final helm values file. Instead there needs to be an # explicit mapping which is done in TWO places: # 1. The ConfigDP is able to map the (unprotected) settings but # does not have access to the protected settings so... # 2. This Powershell script has to perform the mapping for # protected settings. # # This DOES mean that code changes are required both in the # Config DP annd this Powershell script if a new Kubernetes # feature is added. # Do not send protected settings to CCRP $arcAgentryConfigs = ConvertTo-ArcAgentryConfiguration ` -ConfigurationSetting $ConfigurationSetting ` -RedactedProtectedConfiguration @{} ` -CCRP $true ` -Verbose:($PSCmdlet.MyInvocation.BoundParameters["Verbose"].IsPresent -eq $true) ` -Debug:($PSCmdlet.MyInvocation.BoundParameters["Debug"].IsPresent -eq $true) # It is possible to set an empty value for these parameters and then # the code above gets skipped but we still need to remove the empty # values from $PSBoundParameters. if ($PSBoundParameters.ContainsKey('ConfigurationSetting')) { $PSBoundParameters.Remove('ConfigurationSetting') } if ($PSBoundParameters.ContainsKey('ConfigurationProtectedSetting')) { $PSBoundParameters.Remove('ConfigurationProtectedSetting') } $PSBoundParameters.Add('ArcAgentryConfiguration', $arcAgentryConfigs) Write-Verbose "Updating the connected cluster resource...." $CCResponse = Az.ConnectedKubernetes.internal\Set-AzConnectedKubernetes @PSBoundParameters if ((-not $WhatIfPreference) -and (-not $CCResponse)) { Write-Error "Failed to update the 'Kubernetes - Azure Arc' resource" return } # Wait for the agent state to settle before proceeding. If it doesn't, # we'll continue anyway - but remember and throw an error at the end. $agentsInTerminalState = $true if ($PSCmdlet.ShouldProcess($ClusterName, "Check agent state of the connected cluster")) { $timeout = [datetime]::Now.AddMinutes(60) for (;;) { $CCResponse = Get-AzConnectedKubernetes -ResourceGroupName $ResourceGroupName -ClusterName $ClusterName @CommonPSBoundParameters if ($null -eq $CCResponse.ArcAgentProfileAgentState) { Write-Verbose "No agent configuration in progress." break } if ($CCResponse.ArcAgentProfileAgentState -eq "Succeeded") { Write-Verbose "Cluster agent configuration succeeded." break } if ($CCResponse.ArcAgentProfileAgentState -eq "Failed") { Write-Error "Cluster agent configuration failed." break } if ([datetime]::Now -ge $timeout) { Write-Error "Cluster agent configuration timed out after 60 minutes." $agentsInTerminalState = $false break } Write-Verbose "Cluster agent configuration is in progress..." Start-Sleep -Seconds 30 } } $arcAgentryConfigs = ConvertTo-ArcAgentryConfiguration ` -ConfigurationSetting $ConfigurationSetting ` -RedactedProtectedConfiguration $RedactedProtectedConfiguration ` -CCRP $false ` -Verbose:($PSCmdlet.MyInvocation.BoundParameters["Verbose"].IsPresent -eq $true) ` -Debug:($PSCmdlet.MyInvocation.BoundParameters["Debug"].IsPresent -eq $true) # Convert the $Response object into a nested hashtable. Write-Debug "PUT response: $CCResponse" $Response = ConvertFrom-Json "$CCResponse" $Response = ConvertTo-Hashtable $Response # Whatif may return empty response if (-not $Response) { $Response = @{} } if (-not $Response.ContainsKey('properties')) { $Response['properties'] = @{} } $Response['properties']['arcAgentryConfigurations'] = $arcAgentryConfigs # Retrieving Helm chart OCI (Open Container Initiative) Artifact location Write-Debug "Retrieving Helm chart OCI (Open Container Initiative) Artifact location." $ResponseStr = $Response | ConvertTo-Json -Depth 10 Write-Debug "PUT response: $ResponseStr" Write-Verbose "Preparing helm ...." if ($PSCmdlet.ShouldProcess('configDP', 'get helm values from config DP')) { $helmValuesDp = Get-HelmValuesFromConfigDP ` -configDPEndpoint $configDPEndpoint ` -releaseTrain $ReleaseTrain ` -requestBody $ResponseStr ` -Verbose:($PSCmdlet.MyInvocation.BoundParameters["Verbose"].IsPresent -eq $true) ` -Debug:($PSCmdlet.MyInvocation.BoundParameters["Debug"].IsPresent -eq $true) Write-Debug "helmValuesDp: $helmValuesDp" Write-Debug "OCI Artifact location: ${helmValuesDp.repositoryPath}." $registryPath = if ($env:HELMREGISTRY) { $env:HELMREGISTRY } else { $helmValuesDp.repositoryPath } Write-Debug "RegistryPath: ${registryPath}." $helmValuesContent = $helmValuesDp.helmValuesContent Write-Debug "Helm values: ${helmValuesContent}." $optionsFromDp = "" foreach ($field in $helmValuesContent.PSObject.Properties) { if ($field.Value.StartsWith($ProtectedSettingsPlaceholderValue)) { $parsedValue = $field.Value.Split(":") # "${ProtectedSettingsPlaceholderValue}:${feature}:${setting}" $field.Value = $ConfigurationProtectedSetting[$parsedValue[1]][$parsedValue[2]] } if ($field.Name -eq "global.proxyCert") { $optionsFromDp += " --set-file $($field.Name)=$($field.Value)" } $optionsFromDp += " --set $($field.Name)=$($field.Value)" } # In helm, priority is given to new values, so we append $options contains user input last $options = $optionsFromDp + $options # Set agent version in registry path if ($ExistConnectedKubernetes.AgentVersion) { $repositoryPath = $repositoryPath -replace "(?<=:).*", $ExistConnectedKubernetes.AgentVersion } } if ($PSCmdlet.ShouldProcess('configDP', 'get helm chart path')) { # Get helm chart path (within the OCI registry). $chartPath = Get-HelmChartPath ` -registryPath $registryPath ` -kubeConfig $KubeConfig ` -kubeContext $KubeContext ` -helmClientLocation $HelmClientLocation ` -Verbose:($PSCmdlet.MyInvocation.BoundParameters["Verbose"].IsPresent -eq $true) ` -Debug:($PSCmdlet.MyInvocation.BoundParameters["Debug"].IsPresent -eq $true) if (Test-Path Env:HELMCHART) { $ChartPath = Get-ChildItem -Path $Env:HELMCHART } } # Get current helm values if ($PSCmdlet.ShouldProcess($ClusterName, "Get current helm values")) { $userValuesLocation = Get-HelmValue ` -HelmClientLocation $HelmClientLocation ` -Namespace $ReleaseInstallNamespace ` -KubeConfig $KubeConfig ` -KubeContext $KubeContext ` -Verbose:($PSCmdlet.MyInvocation.BoundParameters["Verbose"].IsPresent -eq $true) ` -Debug:($PSCmdlet.MyInvocation.BoundParameters["Debug"].IsPresent -eq $true) } Write-Verbose "Executing helm upgrade, this can take a few minutes ...." Write-Debug $options -ErrorAction Continue if ($DebugPreference -eq "Continue") { $options += " --debug" } if ($PSCmdlet.ShouldProcess($ClusterName, "Update Kubernetes cluster with Azure Arc")) { try { helm upgrade ` azure-arc ` $ChartPath ` --namespace $ReleaseInstallNamespace ` -f $userValuesLocation ` --wait (-split $options) | Out-Null } catch { throw "Unable to install helm release" } } # If there was a problem with agent state, throw the error now. if ($agentsInTerminalState -eq $false) { throw "Timed out waiting for Agent State to reach terminal state." } Return $CCResponse } } function Merge-MaybeNullInput { [Microsoft.Azure.PowerShell.Cmdlets.ConnectedKubernetes.DoNotExportAttribute()] param( [Microsoft.Azure.PowerShell.Cmdlets.ConnectedKubernetes.Models.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 # MIIoLQYJKoZIhvcNAQcCoIIoHjCCKBoCAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCAVDTkvrzjlaRfL # 3dyb10VQiIfEu5oe7my6DrVTU/MiH6CCDXYwggX0MIID3KADAgECAhMzAAAEBGx0 # 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 # MAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIFv44Trv840fSQZSPLG3Sp67 # TZjFHGKm8Ql0BKIZhuKiMEIGCisGAQQBgjcCAQwxNDAyoBSAEgBNAGkAYwByAG8A # cwBvAGYAdKEagBhodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20wDQYJKoZIhvcNAQEB # BQAEggEAYJKx3RKpskt6/MZmvVgyPoNU7gOa6og1rctki5tVm67ApN4WAgxwOa63 # NPsB8qUhtocHWb31FZvPl19cE3bPPAD3ce6dUjCewc6fPI2OSz9ln7eUvro7jnGX # Ja8p7vm351Qdxg2hKh2spDZpVKAu8OhbW+fkkomm01F8npERvPORxiC/wP/2dYni # 2V+4QxkwZTaLOaDNI5fXCYt8EgWfaQz1hLHegPWkAQy6v+hVJz0mJ9YPlnvXI3+d # YZKVm+RqoR9zz5LknbHrOlLR0qpel+08Ctn5jm9nrZi8ltOP1IyFen6lT6iKPM22 # NNiiNe37Brnh99ya4t3TORaj66p7IKGCF5cwgheTBgorBgEEAYI3AwMBMYIXgzCC # F38GCSqGSIb3DQEHAqCCF3AwghdsAgEDMQ8wDQYJYIZIAWUDBAIBBQAwggFSBgsq # hkiG9w0BCRABBKCCAUEEggE9MIIBOQIBAQYKKwYBBAGEWQoDATAxMA0GCWCGSAFl # AwQCAQUABCD+XaBwnc28rETrx+e6JeZSYtuybARBLWuf3QDhMQIFPgIGZ/evqEsf # GBMyMDI1MDQzMDAyMjkwMS40NjVaMASAAgH0oIHRpIHOMIHLMQswCQYDVQQGEwJV # UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UE # ChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSUwIwYDVQQLExxNaWNyb3NvZnQgQW1l # cmljYSBPcGVyYXRpb25zMScwJQYDVQQLEx5uU2hpZWxkIFRTUyBFU046QTQwMC0w # NUUwLUQ5NDcxJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2Wg # ghHtMIIHIDCCBQigAwIBAgITMwAAAgJ5UHQhFH24oQABAAACAjANBgkqhkiG9w0B # AQsFADB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UE # BxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYD # VQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDAeFw0yNTAxMzAxOTQy # NDRaFw0yNjA0MjIxOTQyNDRaMIHLMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2Fz # aGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENv # cnBvcmF0aW9uMSUwIwYDVQQLExxNaWNyb3NvZnQgQW1lcmljYSBPcGVyYXRpb25z # MScwJQYDVQQLEx5uU2hpZWxkIFRTUyBFU046QTQwMC0wNUUwLUQ5NDcxJTAjBgNV # BAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2UwggIiMA0GCSqGSIb3DQEB # AQUAA4ICDwAwggIKAoICAQC3eSp6cucUGOkcPg4vKWKJfEQeshK2ZBsYU1tDWvQu # 6L9lp+dnqrajIdNeH1HN3oz3iiGoJWuN2HVNZkcOt38aWGebM0gUUOtPjuLhuO5d # 67YpQsHBJAWhcve/MVdoQPj1njiAjSiOrL8xFarFLI46RH8NeDhAPXcJpWn7AIzC # yIjZOaJ2DWA+6QwNzwqjBgIpf1hWFwqHvPEedy0notXbtWfT9vCSL9sdDK6K/HH9 # HsaY5wLmUUB7SfuLGo1OWEm6MJyG2jixqi9NyRoypdF8dRyjWxKRl2JxwvbetlDT # io66XliTOckq2RgM+ZocZEb6EoOdtd0XKh3Lzx29AhHxlk+6eIwavlHYuOLZDKod # POVN6j1IJ9brolY6mZboQ51Oqe5nEM5h/WJX28GLZioEkJN8qOe5P5P2Yx9HoOqL # ugX00qCzxq4BDm8xH85HKxvKCO5KikopaRGGtQlXjDyusMWlrHcySt56DhL4dcVn # n7dFvL50zvQlFZMhVoehWSQkkWuUlCCqIOrTe7RbmnbdJosH+7lC+n53gnKy4OoZ # zuUeqzCnSB1JNXPKnJojP3De5xwspi5tUvQFNflfGTsjZgQAgDBdg/DO0TGgLRDK # vZQCZ5qIuXpQRyg37yc51e95z8U2mysU0XnSpWeigHqkyOAtDfcIpq5Gv7HV+da2 # RwIDAQABo4IBSTCCAUUwHQYDVR0OBBYEFNoGubUPjP2f8ifkIKvwy1rlSHTZMB8G # A1UdIwQYMBaAFJ+nFV0AXmJdg/Tl0mWnG1M1GelyMF8GA1UdHwRYMFYwVKBSoFCG # Tmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY3Jvc29mdCUy # MFRpbWUtU3RhbXAlMjBQQ0ElMjAyMDEwKDEpLmNybDBsBggrBgEFBQcBAQRgMF4w # XAYIKwYBBQUHMAKGUGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY2Vy # dHMvTWljcm9zb2Z0JTIwVGltZS1TdGFtcCUyMFBDQSUyMDIwMTAoMSkuY3J0MAwG # A1UdEwEB/wQCMAAwFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwgwDgYDVR0PAQH/BAQD # AgeAMA0GCSqGSIb3DQEBCwUAA4ICAQCD83aFQUxN37HkOoJDM1maHFZVUGcqTQcP # nOD6UoYRMmDKv0GabHlE82AYgLPuVlukn7HtJPF2z0jnTgAfRMn26JFLPG7O/XbK # K25hrBPJ30lBuwjATVt58UA1BWo7lsmnyrur/6h8AFzrXyrXtlvzQYqaRYY9k0UF # Y5GM+n9YaEEK2D268e+a+HDmWe+tYL2H+9O4Q1MQLag+ciNwLkj/+QlxpXiWou9K # vAP0tIk+fH8F3ww5VOTi9aZ9+qPjszw31H4ndtivBZaH5s5boJmH2JbtMuf2y7hS # dJdE0UW2B0FEZPLImemlKhslJNVqEO7RPgl7c81QuVSO58ffpmbwtSxhYrES3VsP # glXn9ODF7DqmPMG/GysB4o/QkpNUq+wS7bORTNzqHMtH+ord2YSma+1byWBr/izI # KggOCdEzaZDfym12GM6a4S+Iy6AUIp7/KIpAmfWfXrcMK7V7EBzxoezkLREEWI4X # tPwpEBntOa1oDH3Z/+dRxsxL0vgya7jNfrO7oizTAln/2ZBYB9ioUeobj5AGL45m # 2mcKSk7HE5zUReVkILpYKBQ5+X/8jFO1/pZyqzQeI1/oJ/RLoic1SieLXfET9EWZ # IBjZMZ846mDbp1ynK9UbNiCjSwmTF509Yn9M47VQsxsv1olQu51rVVHkSNm+rTrL # wK1tvhv0mTCCB3EwggVZoAMCAQICEzMAAAAVxedrngKbSZkAAAAAABUwDQYJKoZI # 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 # MCUGA1UECxMeblNoaWVsZCBUU1MgRVNOOkE0MDAtMDVFMC1EOTQ3MSUwIwYDVQQD # ExxNaWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2aWNloiMKAQEwBwYFKw4DAhoDFQBJ # iUhpCWA/3X/jZyIy0ye6RJwLzqCBgzCBgKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYD # VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1w # IFBDQSAyMDEwMA0GCSqGSIb3DQEBCwUAAgUA67vhtzAiGA8yMDI1MDQyOTIzMzkz # NVoYDzIwMjUwNDMwMjMzOTM1WjB3MD0GCisGAQQBhFkKBAExLzAtMAoCBQDru+G3 # AgEAMAoCAQACAgfhAgH/MAcCAQACAhLkMAoCBQDrvTM3AgEAMDYGCisGAQQBhFkK # BAIxKDAmMAwGCisGAQQBhFkKAwKgCjAIAgEAAgMHoSChCjAIAgEAAgMBhqAwDQYJ # KoZIhvcNAQELBQADggEBAH/eH2LTA9iBAA9IXJ71aWb5B8XJt2Uhy3ac9Ry8GEsP # Wfp9DVm/pyAhKLeXEkbszziqkq6G/rAAn0/7Rqtzn4rm9d2YObxBY4sLAf5e1Kv6 # T+B107PpJXOElaGFvQ3jD440/YBTWU545w0TUV3K4uQMqcYSQhJjN4BCZlMkkqq6 # iMkPcmOkVVJy69FVUFgck+PvNIkzVsDqRysF0IFCKhP6wjrqhmp8i5vTGx5tbg6M # EpogrTngX75Lbg0DfQUHf7K8vhKDZwsGHmA7m/3HuSkFfWOLBMZ+DV/8Rgog7Tvq # eKnv6Vx+fXZeLy+yti3VjxHMFWC2Dq9OGebBGW2R2lExggQNMIIECQIBATCBkzB8 # MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVk # bW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1N # aWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMAITMwAAAgJ5UHQhFH24oQABAAAC # AjANBglghkgBZQMEAgEFAKCCAUowGgYJKoZIhvcNAQkDMQ0GCyqGSIb3DQEJEAEE # MC8GCSqGSIb3DQEJBDEiBCBtwiRVy5sTjrRnZMvsyz+1vHfNwoX2dEMyfqLwDcbU # XDCB+gYLKoZIhvcNAQkQAi8xgeowgecwgeQwgb0EIPON6gEYB5bLzXLWuUmL8Zd8 # xXAsqXksedFyolfMlF/sMIGYMIGApH4wfDELMAkGA1UEBhMCVVMxEzARBgNVBAgT # Cldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29m # dCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENB # IDIwMTACEzMAAAICeVB0IRR9uKEAAQAAAgIwIgQgehMoHtZIyCtc/7VcZoAaBY1n # 7fSlCrxjRZaQZEYkW7swDQYJKoZIhvcNAQELBQAEggIANyIJFBMvyYh9VfmhTjQn # PMXLegcLunfww4Mdd1hE1e5AjkYT+PYQoJcD5fCNYJwvY6unHneeZGX2jeVEwOm6 # OTYHmSa32y3kDftzI68M2ibVU+6j3BdIpAihR6xpzjP4VZnweSx4iIcOjFmF10ri # fbsNK/BtpOXt39QRDngr0N5W2l+TRxzxZ4fbJThftbOy+rN2GJUGeaG66yWCziEA # ONoLdBkKRFlSkOT8upTxzLZDmjp4Nal7LwVQJ19+ayFXh4bRL1iSeosqX72q20ba # q0onCDxdW90x06+SB4qfquWm6NEyHH6CUSqJhNSB0r9suQi7mhce2zm59DjNpR7a # I1Kwu8GaQT5qPF3OLmlbbLiiUDlMlUyQM9iAXePgDWjtbL3EY+7B/jKJznxWDDnV # MsV51pZ8MYpS/mjhGehwf7vwShOO6EB3K0vIeMjVHUUZDflBR4gni/HLPy9hGaOc # QaNc7fNl/ANxekI5+4nM779JpsTO1OpSZ1g3ikKqzs/SyEU9f59kwyDb34yjTDdJ # gQaM2rW33VDFdNX7uoFAIchWKH2421NFoUWwcmeuNCXqf2GyuXehvE9hP+MgbQEE # IUGvkkPCeTgbE4OPAvHNFPIAmOH6MfSkI9XeRUEWSqT097vu6wIPTN3x+A+Q/B3Y # N/xa2+fc0i9h5RBSUqw8uJE= # SIG # End signature block |