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 #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 # Perform DP health check $configDpinfo = Get-ConfigDPEndpoint -location $Location -Cloud $cloudMetadata $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 # 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 # 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 # 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 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 # MIIoPAYJKoZIhvcNAQcCoIIoLTCCKCkCAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCDr1zkJFDk+oewo # 8CtqqacoHEbhOFFdnsIn/UPNdxZfaKCCDYUwggYDMIID66ADAgECAhMzAAADri01 # UchTj1UdAAAAAAOuMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD # VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p # bmcgUENBIDIwMTEwHhcNMjMxMTE2MTkwODU5WhcNMjQxMTE0MTkwODU5WjB0MQsw # CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u # ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB # AQD0IPymNjfDEKg+YyE6SjDvJwKW1+pieqTjAY0CnOHZ1Nj5irGjNZPMlQ4HfxXG # yAVCZcEWE4x2sZgam872R1s0+TAelOtbqFmoW4suJHAYoTHhkznNVKpscm5fZ899 # QnReZv5WtWwbD8HAFXbPPStW2JKCqPcZ54Y6wbuWV9bKtKPImqbkMcTejTgEAj82 # 6GQc6/Th66Koka8cUIvz59e/IP04DGrh9wkq2jIFvQ8EDegw1B4KyJTIs76+hmpV # M5SwBZjRs3liOQrierkNVo11WuujB3kBf2CbPoP9MlOyyezqkMIbTRj4OHeKlamd # WaSFhwHLJRIQpfc8sLwOSIBBAgMBAAGjggGCMIIBfjAfBgNVHSUEGDAWBgorBgEE # AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUhx/vdKmXhwc4WiWXbsf0I53h8T8w # VAYDVR0RBE0wS6RJMEcxLTArBgNVBAsTJE1pY3Jvc29mdCBJcmVsYW5kIE9wZXJh # dGlvbnMgTGltaXRlZDEWMBQGA1UEBRMNMjMwMDEyKzUwMTgzNjAfBgNVHSMEGDAW # gBRIbmTlUAXTgqoXNzcitW2oynUClTBUBgNVHR8ETTBLMEmgR6BFhkNodHRwOi8v # d3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NybC9NaWNDb2RTaWdQQ0EyMDExXzIw # MTEtMDctMDguY3JsMGEGCCsGAQUFBwEBBFUwUzBRBggrBgEFBQcwAoZFaHR0cDov # L3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9jZXJ0cy9NaWNDb2RTaWdQQ0EyMDEx # XzIwMTEtMDctMDguY3J0MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggIB # AGrJYDUS7s8o0yNprGXRXuAnRcHKxSjFmW4wclcUTYsQZkhnbMwthWM6cAYb/h2W # 5GNKtlmj/y/CThe3y/o0EH2h+jwfU/9eJ0fK1ZO/2WD0xi777qU+a7l8KjMPdwjY # 0tk9bYEGEZfYPRHy1AGPQVuZlG4i5ymJDsMrcIcqV8pxzsw/yk/O4y/nlOjHz4oV # APU0br5t9tgD8E08GSDi3I6H57Ftod9w26h0MlQiOr10Xqhr5iPLS7SlQwj8HW37 # ybqsmjQpKhmWul6xiXSNGGm36GarHy4Q1egYlxhlUnk3ZKSr3QtWIo1GGL03hT57 # xzjL25fKiZQX/q+II8nuG5M0Qmjvl6Egltr4hZ3e3FQRzRHfLoNPq3ELpxbWdH8t # Nuj0j/x9Crnfwbki8n57mJKI5JVWRWTSLmbTcDDLkTZlJLg9V1BIJwXGY3i2kR9i # 5HsADL8YlW0gMWVSlKB1eiSlK6LmFi0rVH16dde+j5T/EaQtFz6qngN7d1lvO7uk # 6rtX+MLKG4LDRsQgBTi6sIYiKntMjoYFHMPvI/OMUip5ljtLitVbkFGfagSqmbxK # 7rJMhC8wiTzHanBg1Rrbff1niBbnFbbV4UDmYumjs1FIpFCazk6AADXxoKCo5TsO # zSHqr9gHgGYQC2hMyX9MGLIpowYCURx3L7kUiGbOiMwaMIIHejCCBWKgAwIBAgIK # YQ6Q0gAAAAAAAzANBgkqhkiG9w0BAQsFADCBiDELMAkGA1UEBhMCVVMxEzARBgNV # BAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jv # c29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJvb3QgQ2VydGlm # aWNhdGUgQXV0aG9yaXR5IDIwMTEwHhcNMTEwNzA4MjA1OTA5WhcNMjYwNzA4MjEw # OTA5WjB+MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UE # BxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSgwJgYD # VQQDEx9NaWNyb3NvZnQgQ29kZSBTaWduaW5nIFBDQSAyMDExMIICIjANBgkqhkiG # 9w0BAQEFAAOCAg8AMIICCgKCAgEAq/D6chAcLq3YbqqCEE00uvK2WCGfQhsqa+la # UKq4BjgaBEm6f8MMHt03a8YS2AvwOMKZBrDIOdUBFDFC04kNeWSHfpRgJGyvnkmc # 6Whe0t+bU7IKLMOv2akrrnoJr9eWWcpgGgXpZnboMlImEi/nqwhQz7NEt13YxC4D # dato88tt8zpcoRb0RrrgOGSsbmQ1eKagYw8t00CT+OPeBw3VXHmlSSnnDb6gE3e+ # lD3v++MrWhAfTVYoonpy4BI6t0le2O3tQ5GD2Xuye4Yb2T6xjF3oiU+EGvKhL1nk # kDstrjNYxbc+/jLTswM9sbKvkjh+0p2ALPVOVpEhNSXDOW5kf1O6nA+tGSOEy/S6 # A4aN91/w0FK/jJSHvMAhdCVfGCi2zCcoOCWYOUo2z3yxkq4cI6epZuxhH2rhKEmd # X4jiJV3TIUs+UsS1Vz8kA/DRelsv1SPjcF0PUUZ3s/gA4bysAoJf28AVs70b1FVL # 5zmhD+kjSbwYuER8ReTBw3J64HLnJN+/RpnF78IcV9uDjexNSTCnq47f7Fufr/zd # sGbiwZeBe+3W7UvnSSmnEyimp31ngOaKYnhfsi+E11ecXL93KCjx7W3DKI8sj0A3 # T8HhhUSJxAlMxdSlQy90lfdu+HggWCwTXWCVmj5PM4TasIgX3p5O9JawvEagbJjS # 4NaIjAsCAwEAAaOCAe0wggHpMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBRI # bmTlUAXTgqoXNzcitW2oynUClTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTAL # BgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBRyLToCMZBD # uRQFTuHqp8cx0SOJNDBaBgNVHR8EUzBRME+gTaBLhklodHRwOi8vY3JsLm1pY3Jv # c29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFf # MDNfMjIuY3JsMF4GCCsGAQUFBwEBBFIwUDBOBggrBgEFBQcwAoZCaHR0cDovL3d3 # dy5taWNyb3NvZnQuY29tL3BraS9jZXJ0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFf # MDNfMjIuY3J0MIGfBgNVHSAEgZcwgZQwgZEGCSsGAQQBgjcuAzCBgzA/BggrBgEF # BQcCARYzaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9kb2NzL3ByaW1h # cnljcHMuaHRtMEAGCCsGAQUFBwICMDQeMiAdAEwAZQBnAGEAbABfAHAAbwBsAGkA # YwB5AF8AcwB0AGEAdABlAG0AZQBuAHQALiAdMA0GCSqGSIb3DQEBCwUAA4ICAQBn # 8oalmOBUeRou09h0ZyKbC5YR4WOSmUKWfdJ5DJDBZV8uLD74w3LRbYP+vj/oCso7 # v0epo/Np22O/IjWll11lhJB9i0ZQVdgMknzSGksc8zxCi1LQsP1r4z4HLimb5j0b # pdS1HXeUOeLpZMlEPXh6I/MTfaaQdION9MsmAkYqwooQu6SpBQyb7Wj6aC6VoCo/ # KmtYSWMfCWluWpiW5IP0wI/zRive/DvQvTXvbiWu5a8n7dDd8w6vmSiXmE0OPQvy # CInWH8MyGOLwxS3OW560STkKxgrCxq2u5bLZ2xWIUUVYODJxJxp/sfQn+N4sOiBp # mLJZiWhub6e3dMNABQamASooPoI/E01mC8CzTfXhj38cbxV9Rad25UAqZaPDXVJi # hsMdYzaXht/a8/jyFqGaJ+HNpZfQ7l1jQeNbB5yHPgZ3BtEGsXUfFL5hYbXw3MYb # BL7fQccOKO7eZS/sl/ahXJbYANahRr1Z85elCUtIEJmAH9AAKcWxm6U/RXceNcbS # oqKfenoi+kiVH6v7RyOA9Z74v2u3S5fi63V4GuzqN5l5GEv/1rMjaHXmr/r8i+sL # gOppO6/8MO0ETI7f33VtY5E90Z1WTk+/gFcioXgRMiF670EKsT/7qMykXcGhiJtX # cVZOSEXAQsmbdlsKgEhr/Xmfwb1tbWrJUnMTDXpQzTGCGg0wghoJAgEBMIGVMH4x # CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRt # b25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01p # Y3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMTECEzMAAAOuLTVRyFOPVR0AAAAA # A64wDQYJYIZIAWUDBAIBBQCgga4wGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQw # HAYKKwYBBAGCNwIBCzEOMAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEINIY # 0mSi7OPgF3AqXbpQTQop93e6gCExCqMVvna3KmBkMEIGCisGAQQBgjcCAQwxNDAy # oBSAEgBNAGkAYwByAG8AcwBvAGYAdKEagBhodHRwOi8vd3d3Lm1pY3Jvc29mdC5j # b20wDQYJKoZIhvcNAQEBBQAEggEAZyb3HPHOzOI9+oGJKceESbr1K/qv9D+l8Jg/ # geRKXdRUb/Aokn4MT51f6Z/aZIaN4kJBR5DryT+sjp2Zc2VARtNifM/CrEVkoERS # mIlqnMRpD2upKHXvp4LVZPl9dROv860A/iqpVMn2xqd8rWwuxuFow0arAFAm3u65 # jOftEHTsfsfhAEokPZ+doiu2YUL8iOCIfHRibbN7t13rWAy5yprJ/6OEQUb0uJ6O # QLaYqyjGe3rEC3i3tOXofrsC6W0bu1Ovp0NLmwdfoZAs2PPh+uF5UfrBPvXhAA0d # /V3bQsijcCuEqH0m4CkXI8Kmtd/zBADmqqwsi00be3nMMJLZL6GCF5cwgheTBgor # BgEEAYI3AwMBMYIXgzCCF38GCSqGSIb3DQEHAqCCF3AwghdsAgEDMQ8wDQYJYIZI # AWUDBAIBBQAwggFSBgsqhkiG9w0BCRABBKCCAUEEggE9MIIBOQIBAQYKKwYBBAGE # WQoDATAxMA0GCWCGSAFlAwQCAQUABCAV4UTF/mmzRqCyDiysc0cK949lxCLSkn+Q # STzVD27CzwIGZuOwm4KhGBMyMDI0MTAwODA1MDcwMi45MzdaMASAAgH0oIHRpIHO # MIHLMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH # UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSUwIwYDVQQL # ExxNaWNyb3NvZnQgQW1lcmljYSBPcGVyYXRpb25zMScwJQYDVQQLEx5uU2hpZWxk # IFRTUyBFU046OEQwMC0wNUUwLUQ5NDcxJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1l # LVN0YW1wIFNlcnZpY2WgghHtMIIHIDCCBQigAwIBAgITMwAAAfPFCkOuA8wdMQAB # AAAB8zANBgkqhkiG9w0BAQsFADB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2Fz # aGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENv # cnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAx # MDAeFw0yMzEyMDYxODQ2MDJaFw0yNTAzMDUxODQ2MDJaMIHLMQswCQYDVQQGEwJV # UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UE # ChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSUwIwYDVQQLExxNaWNyb3NvZnQgQW1l # cmljYSBPcGVyYXRpb25zMScwJQYDVQQLEx5uU2hpZWxkIFRTUyBFU046OEQwMC0w # NUUwLUQ5NDcxJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2Uw # ggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQD+n6ba4SuB9iSO5WMhbngq # YAb+z3IfzNpZIWS/sgfXhlLYmGnsUtrGX3OVcg+8krJdixuNUMO7ZAOqCZsXUjOz # 8zcn1aUD5D2r2PhzVKjHtivWGgGj4x5wqWe1Qov3vMz8WHsKsfadIlWjfBMnVKVo # mOybQ7+2jc4afzj2XJQQSmE9jQRoBogDwmqZakeYnIx0EmOuucPr674T6/YaTPiI # YlGf+XV2u6oQHAkMG56xYPQikitQjjNWHADfBqbBEaqppastxpRNc4id2S1xVQxc # QGXjnAgeeVbbPbAoELhbw+z3VetRwuEFJRzT6hbWEgvz9LMYPSbioHL8w+ZiWo3x # uw3R7fJsqe7pqsnjwvniP7sfE1utfi7k0NQZMpviOs//239H6eA6IOVtF8w66ipE # 71EYrcSNrOGlTm5uqq+syO1udZOeKM0xY728NcGDFqnjuFPbEEm6+etZKftU9jxL # CSzqXOVOzdqA8O5Xa3E41j3s7MlTF4Q7BYrQmbpxqhTvfuIlYwI2AzeO3OivcezJ # wBj2FQgTiVHacvMQDgSA7E5vytak0+MLBm0AcW4IPer8A4gOGD9oSprmyAu1J6wF # kBrf2Sjn+ieNq6Fx0tWj8Ipg3uQvcug37jSadF6q1rUEaoPIajZCGVk+o5wn6rt+ # cwdJ39REU43aWCwn0C+XxwIDAQABo4IBSTCCAUUwHQYDVR0OBBYEFMNkFfalEVEM # jA3ApoUx9qDrDQokMB8GA1UdIwQYMBaAFJ+nFV0AXmJdg/Tl0mWnG1M1GelyMF8G # A1UdHwRYMFYwVKBSoFCGTmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMv # Y3JsL01pY3Jvc29mdCUyMFRpbWUtU3RhbXAlMjBQQ0ElMjAyMDEwKDEpLmNybDBs # BggrBgEFBQcBAQRgMF4wXAYIKwYBBQUHMAKGUGh0dHA6Ly93d3cubWljcm9zb2Z0 # LmNvbS9wa2lvcHMvY2VydHMvTWljcm9zb2Z0JTIwVGltZS1TdGFtcCUyMFBDQSUy # MDIwMTAoMSkuY3J0MAwGA1UdEwEB/wQCMAAwFgYDVR0lAQH/BAwwCgYIKwYBBQUH # AwgwDgYDVR0PAQH/BAQDAgeAMA0GCSqGSIb3DQEBCwUAA4ICAQDfxByP/NH+79vc # 3liO4c7nXM/UKFcAm5w61FxRxPxCXRXliNjZ7sDqNP0DzUTBU9tS5DqkqRSiIV15 # j7q8e6elg8/cD3bv0sW4Go9AML4lhA5MBg3wzKdihfJ0E/HIqcHX11mwtbpTiC2s # gAUh7+OZnb9TwJE7pbEBPJQUxxuCiS5/r0s2QVipBmi/8MEW2eIi4mJ+vHI5DCaA # GooT4A15/7oNj9zyzRABTUICNNrS19KfryEN5dh5kqOG4Qgca9w6L7CL+SuuTZi0 # SZ8Zq65iK2hQ8IMAOVxewCpD4lZL6NDsVNSwBNXOUlsxOAO3G0wNT+cBug/HD43B # 7E2odVfs6H2EYCZxUS1rgReGd2uqQxgQ2wrMuTb5ykO+qd+4nhaf/9SN3getomtQ # n5IzhfCkraT1KnZF8TI3ye1Z3pner0Cn/p15H7wNwDkBAiZ+2iz9NUEeYLfMGm9v # ErDVBDRMjGsE/HqqY7QTSTtDvU7+zZwRPGjiYYUFXT+VgkfdHiFpKw42Xsm0MfL5 # aOa31FyCM17/pPTIKTRiKsDF370SwIwZAjVziD/9QhEFBu9pojFULOZvzuL5iSEJ # IcqopVAwdbNdroZi2HN8nfDjzJa8CMTkQeSfQsQpKr83OhBmE3MF2sz8gqe3loc0 # 5DW8JNvZ328Jps3LJCALt0rQPJYnOzCCB3EwggVZoAMCAQICEzMAAAAVxedrngKb # SZkAAAAAABUwDQYJKoZIhvcNAQELBQAwgYgxCzAJBgNVBAYTAlVTMRMwEQYDVQQI # EwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3Nv # ZnQgQ29ycG9yYXRpb24xMjAwBgNVBAMTKU1pY3Jvc29mdCBSb290IENlcnRpZmlj # YXRlIEF1dGhvcml0eSAyMDEwMB4XDTIxMDkzMDE4MjIyNVoXDTMwMDkzMDE4MzIy # NVowfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcT # B1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UE # AxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTAwggIiMA0GCSqGSIb3DQEB # AQUAA4ICDwAwggIKAoICAQDk4aZM57RyIQt5osvXJHm9DtWC0/3unAcH0qlsTnXI # yjVX9gF/bErg4r25PhdgM/9cT8dm95VTcVrifkpa/rg2Z4VGIwy1jRPPdzLAEBjo # YH1qUoNEt6aORmsHFPPFdvWGUNzBRMhxXFExN6AKOG6N7dcP2CZTfDlhAnrEqv1y # aa8dq6z2Nr41JmTamDu6GnszrYBbfowQHJ1S/rboYiXcag/PXfT+jlPP1uyFVk3v # 3byNpOORj7I5LFGc6XBpDco2LXCOMcg1KL3jtIckw+DJj361VI/c+gVVmG1oO5pG # ve2krnopN6zL64NF50ZuyjLVwIYwXE8s4mKyzbnijYjklqwBSru+cakXW2dg3viS # kR4dPf0gz3N9QZpGdc3EXzTdEonW/aUgfX782Z5F37ZyL9t9X4C626p+Nuw2TPYr # bqgSUei/BQOj0XOmTTd0lBw0gg/wEPK3Rxjtp+iZfD9M269ewvPV2HM9Q07BMzlM # jgK8QmguEOqEUUbi0b1qGFphAXPKZ6Je1yh2AuIzGHLXpyDwwvoSCtdjbwzJNmSL # W6CmgyFdXzB0kZSU2LlQ+QuJYfM2BjUYhEfb3BvR/bLUHMVr9lxSUV0S2yW6r1AF # emzFER1y7435UsSFF5PAPBXbGjfHCBUYP3irRbb1Hode2o+eFnJpxq57t7c+auIu # rQIDAQABo4IB3TCCAdkwEgYJKwYBBAGCNxUBBAUCAwEAATAjBgkrBgEEAYI3FQIE # FgQUKqdS/mTEmr6CkTxGNSnPEP8vBO4wHQYDVR0OBBYEFJ+nFV0AXmJdg/Tl0mWn # G1M1GelyMFwGA1UdIARVMFMwUQYMKwYBBAGCN0yDfQEBMEEwPwYIKwYBBQUHAgEW # M2h0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvRG9jcy9SZXBvc2l0b3J5 # Lmh0bTATBgNVHSUEDDAKBggrBgEFBQcDCDAZBgkrBgEEAYI3FAIEDB4KAFMAdQBi # AEMAQTALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBTV # 9lbLj+iiXGJo0T2UkFvXzpoYxDBWBgNVHR8ETzBNMEugSaBHhkVodHRwOi8vY3Js # Lm1pY3Jvc29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXRfMjAx # MC0wNi0yMy5jcmwwWgYIKwYBBQUHAQEETjBMMEoGCCsGAQUFBzAChj5odHRwOi8v # d3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dF8yMDEwLTA2 # LTIzLmNydDANBgkqhkiG9w0BAQsFAAOCAgEAnVV9/Cqt4SwfZwExJFvhnnJL/Klv # 6lwUtj5OR2R4sQaTlz0xM7U518JxNj/aZGx80HU5bbsPMeTCj/ts0aGUGCLu6WZn # OlNN3Zi6th542DYunKmCVgADsAW+iehp4LoJ7nvfam++Kctu2D9IdQHZGN5tggz1 # bSNU5HhTdSRXud2f8449xvNo32X2pFaq95W2KFUn0CS9QKC/GbYSEhFdPSfgQJY4 # rPf5KYnDvBewVIVCs/wMnosZiefwC2qBwoEZQhlSdYo2wh3DYXMuLGt7bj8sCXgU # 6ZGyqVvfSaN0DLzskYDSPeZKPmY7T7uG+jIa2Zb0j/aRAfbOxnT99kxybxCrdTDF # NLB62FD+CljdQDzHVG2dY3RILLFORy3BFARxv2T5JL5zbcqOCb2zAVdJVGTZc9d/ # HltEAY5aGZFrDZ+kKNxnGSgkujhLmm77IVRrakURR6nxt67I6IleT53S0Ex2tVdU # CbFpAUR+fKFhbHP+CrvsQWY9af3LwUFJfn6Tvsv4O+S3Fb+0zj6lMVGEvL8CwYKi # excdFYmNcP7ntdAoGokLjzbaukz5m/8K6TT4JDVnK+ANuOaMmdbhIurwJ0I9JZTm # dHRbatGePu1+oDEzfbzL6Xu/OHBE0ZDxyKs6ijoIYn/ZcGNTTY3ugm2lBRDBcQZq # ELQdVTNYs6FwZvKhggNQMIICOAIBATCB+aGB0aSBzjCByzELMAkGA1UEBhMCVVMx # EzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoT # FU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjElMCMGA1UECxMcTWljcm9zb2Z0IEFtZXJp # Y2EgT3BlcmF0aW9uczEnMCUGA1UECxMeblNoaWVsZCBUU1MgRVNOOjhEMDAtMDVF # MC1EOTQ3MSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2aWNloiMK # AQEwBwYFKw4DAhoDFQBu+gYs2LRha5pFO79g3LkfwKRnKKCBgzCBgKR+MHwxCzAJ # BgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25k # MR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jv # c29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwMA0GCSqGSIb3DQEBCwUAAgUA6q8iWjAi # GA8yMDI0MTAwODAzMTUzOFoYDzIwMjQxMDA5MDMxNTM4WjB3MD0GCisGAQQBhFkK # BAExLzAtMAoCBQDqryJaAgEAMAoCAQACAh6EAgH/MAcCAQACAhPoMAoCBQDqsHPa # AgEAMDYGCisGAQQBhFkKBAIxKDAmMAwGCisGAQQBhFkKAwKgCjAIAgEAAgMHoSCh # CjAIAgEAAgMBhqAwDQYJKoZIhvcNAQELBQADggEBABOz3Wz4XQXEW91E4SHI8cSV # NsP2bH14FHWJo50FX1gQ65coJTG6H6+AlawFsX79MoWXFYjy8u73ZwXvpHYpyFUb # 6eWhsTjUPLgzkcbKNx9CewsdjOaKDxrv+OJhDOA1Ft2EMbC03aGcGFq02Z+vOFke # IyduEcWINW1WUaJGnNFPeiFjA6gNa7mWa/DntkWoIpea5yLR6hbrGFSfkOUYZ7VY # Pt0VbsLB4kYpgb+7jhvc2T0M5S04fb9HBlzopIH6qE/KH39AuJLBRB7J9eMNBI5B # O9tkYNcj+JWbM39EsXh21BKzvcEKogb/nFefLA0OfgmOvSaIb1oehbCMutc7Ydkx # ggQNMIIECQIBATCBkzB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3Rv # bjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0 # aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMAITMwAA # AfPFCkOuA8wdMQABAAAB8zANBglghkgBZQMEAgEFAKCCAUowGgYJKoZIhvcNAQkD # MQ0GCyqGSIb3DQEJEAEEMC8GCSqGSIb3DQEJBDEiBCBP7ZD1gHG21VVpddb1aE3W # 8ZERriO37vnxxhc4kJiMYjCB+gYLKoZIhvcNAQkQAi8xgeowgecwgeQwgb0EIBi8 # 2TSLtuG4Vkp8wBmJk/T+RAh841sG/aDOwxg6O2LoMIGYMIGApH4wfDELMAkGA1UE # BhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAc # BgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0 # IFRpbWUtU3RhbXAgUENBIDIwMTACEzMAAAHzxQpDrgPMHTEAAQAAAfMwIgQgJCCs # tcjPEwKhRLuSUfTExa9FHC/586NhiUpwfPOCAXkwDQYJKoZIhvcNAQELBQAEggIA # udcNRRW5xWlHt4TdbKX0oEohINxlvrhZnm2C7yyadYlcj8Tb/YeCo0Fa/TPzitYk # FSmLQHisD2dJNaTAjhhjVJ+oXsgqgSTnnCINCeXE3i2Pibxs3pkJi4PYixRDg91V # uCOWbBFrBp66E0p6B+/SSlbUnhjVvalr1z/6AmIfgh7th8uadyykPtLRUe8ys1Cs # KaDuyVhNu04GrVH3kOidHLN1Z9xRBtviBQ/Pg/BoHVlIsk7bKKxL2EN8/7Y0X7vv # h5gI+ersp7VMlAl6dcHFce9Z3S2rEVJZX8tJTVpHDViVS8ubHntj6+TvGcIwd9yF # LiPWklmI/7eKsMIGKMcYay2AOf3BxUtQkUBoMt3nnVRtSa8siD1i77eJlMq7YgdQ # UarWvQXOyf6KfRFjtijMk4pqNz2FR69UN3HJOGLqsIM4n/8idwc8eH8n17L6b+X2 # DMzTha7ehn4fXAdLPheU4xJeuizPQsOUmwuLn5lkY4F7MyfaCRWchj0O798P39X4 # AuSU1dTRgEHJbCHe3rz6GJ3+HzEUOP1k2shx+RvjpNJsiUzo6XLLbSOBXej2tRES # lbwfYi5jQV68xyQc1OAMzadx8n+ZRBKcsI2y1S0+eB6JcpYzSiFfTwWA+9Nn9AiM # pmsY2Ox1kRs8ytWttR21n09qa8bUx5UbdEtDBP4nxOc= # SIG # End signature block |